AUTHOR: JORDI TARROCH MEJÓN

El archivo H1 consta de 40.060 observaciones efectuadas sobre 31 variables en un hotel de vacaciones (resort), tal y como se describe en Antonio, Almeida y Nunes (2019). Su trabajo de cara a la evaluación de la asignatura consistirá en elaborar tres documentos con las características siguientes.

El primero, común a esta asignatura y a la del Prof. López Zafra, consistirá en: - Describir las operaciones de limpieza y transformación de los datos de cara a la consolidación de los mismos para su posterior aplicación a cada uno de los trabajos específicos que deberá realizar en cada asignatura.

Este documento, “Operaciones preliminares”, tendrá la estructura de un paper: - título (el señalado) - abstract - introducción y objetivos del trabajo a realizar - operaciones comunes para las dos asignaturas: EDA - operaciones específicas correspondientes a Agrupación - operaciones específicas correspondientes a Predicción - conclusiones - referencias bibliográficas.

ORIENTACIÓN en la estructura de Antonio, Almeida y Nunes (2019). No debe reproducir ninguna información que se encuentre en el citado paper, como, por ejemplo, la descripción del archivo y las variables. La extensión máxima de este documento será de 10 pp.

LIBRARIES

##############
#LIBRARIES####
##############
library(readr)
library(skimr)# Beautiful Summarize
library(cleandata)# ordinal encoding
library(onehot)# nominal encoding

library(ggplot2)
library(ggpubr)
library(easyGgplot2)
library(forcats)

library(PerformanceAnalytics) # Correlations
library(corrplot)
library(dplyr) # select

library(factoextra)
library("NbClust")
library(cluster)

library(zoo)
library(ggfortify)
require(forecast)
raw_data<-read_csv("H1.csv")
Parsed with column specification:
cols(
  .default = col_double(),
  ArrivalDateMonth = col_character(),
  Meal = col_character(),
  Country = col_character(),
  MarketSegment = col_character(),
  DistributionChannel = col_character(),
  ReservedRoomType = col_character(),
  AssignedRoomType = col_character(),
  DepositType = col_character(),
  Agent = col_character(),
  Company = col_character(),
  CustomerType = col_character(),
  ReservationStatus = col_character(),
  ReservationStatusDate = col_date(format = "")
)
See spec(...) for full column specifications.

TARGET VARIABLE

unique(raw_data$IsCanceled[raw_data$ReservationStatus == 'Check-Out'])
[1] 0
unique(raw_data$IsCanceled[raw_data$ReservationStatus == 'No-Show'])
[1] 1
unique(raw_data$IsCanceled[raw_data$ReservationStatus == 'Canceled'])
[1] 1

DATA WRANGLING

ENCODING CATEGORICAL VARIABLES

ORDINAL ENCODING

  • ArrivalDateMonth
raw_data_encoded = raw_data
# ORDINAL ENCODING
#ArrivalDateMonth
levels <- c('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December')
raw_data_encoded$ArrivalDateMonth = factor(raw_data_encoded$ArrivalDateMonth, order = TRUE , levels)
x <- as.data.frame(raw_data_encoded$ArrivalDateMonth)
raw_data_encoded$ArrivalDateMonth <- encode_ordinal( x, levels, none='', out.int=FALSE,
               full_print=TRUE)
 raw_data_encoded$ArrivalDateMonth
 August : 4894                    
 July   : 4573                    
 April  : 3609                    
 May    : 3559                    
 October: 3555                    
 March  : 3336                    
 (Other):16534                    
coded 1 cols 12 levels 
 raw_data_encoded$ArrivalDateMonth
 8      : 4894                    
 7      : 4573                    
 4      : 3609                    
 5      : 3559                    
 10     : 3555                    
 3      : 3336                    
 (Other):16534                    
raw_data_encoded$ArrivalDateMonth <- as.numeric(unlist(raw_data_encoded$ArrivalDateMonth))

print(dim(raw_data_encoded))
[1] 40060    31

NOMINAL ENCODING - ONEHOT ENCODING

  • ReservedRoomType
  • AssignedRoomType
  • Meal
  • Country
  • MarketSegment
  • DistributionChannel
  • DepositType
  • CustomerType
# NOMINAL ENCODING - ONEHOT ENCODING

# https://github.com/Zelazny7/onehot
# ReservedRoomType
ReservedRoomType <- as.data.frame(raw_data_encoded$ReservedRoomType)
encoder <- onehot(ReservedRoomType, max_levels = 15, add_NA_factors = FALSE)
ReservedRoomType_onehot<- predict(encoder, ReservedRoomType, stringsAsFactors=TRUE)
raw_data_encoded <- cbind(raw_data_encoded, ReservedRoomType_onehot)
print(dim(raw_data_encoded))
[1] 40060    41
# AssignedRoomType
AssignedRoomType <- as.data.frame(raw_data_encoded$AssignedRoomType)
encoder <- onehot(AssignedRoomType, max_levels = 15, add_NA_factors = FALSE)
AssignedRoomType_onehot<- predict(encoder, AssignedRoomType, stringsAsFactors=TRUE)
raw_data_encoded <- cbind(raw_data_encoded, AssignedRoomType_onehot)
print(dim(raw_data_encoded))
[1] 40060    52
# Meal
Meal <- as.data.frame(raw_data_encoded$Meal)
encoder <- onehot(Meal, max_levels = 15, add_NA_factors = FALSE)
Meal_onehot<- predict(encoder, Meal, stringsAsFactors=TRUE)
raw_data_encoded <- cbind(raw_data_encoded, Meal_onehot)
print(dim(raw_data_encoded))
[1] 40060    57
# Country
Country <- as.data.frame(raw_data_encoded$Country)
encoder <- onehot(Country, max_levels = 130, add_NA_factors = FALSE)
Country_onehot<- predict(encoder, Country, stringsAsFactors=TRUE)
raw_data_encoded <- cbind(raw_data_encoded, Country_onehot)
print(dim(raw_data_encoded))
[1] 40060   183
# MarketSegment
MarketSegment <- as.data.frame(raw_data_encoded$MarketSegment)
encoder <- onehot(MarketSegment, max_levels = 15, add_NA_factors = FALSE)
MarketSegment_onehot<- predict(encoder, MarketSegment, stringsAsFactors=TRUE)
raw_data_encoded <- cbind(raw_data_encoded, MarketSegment_onehot)

# DistributionChannel
DistributionChannel <- as.data.frame(raw_data_encoded$DistributionChannel)
encoder <- onehot(DistributionChannel, max_levels = 15, add_NA_factors = FALSE)
DistributionChannel_onehot<- predict(encoder, DistributionChannel, stringsAsFactors=TRUE)
raw_data_encoded <- cbind(raw_data_encoded, DistributionChannel_onehot)


# DepositType
DepositType <- as.data.frame(raw_data_encoded$DepositType)
encoder <- onehot(DepositType, max_levels = 15, add_NA_factors = FALSE)
DepositType_onehot<- predict(encoder, DepositType, stringsAsFactors=TRUE)
raw_data_encoded <- cbind(raw_data_encoded, DepositType_onehot)


# CustomerType
CustomerType <- as.data.frame(raw_data_encoded$CustomerType)
encoder <- onehot(CustomerType, max_levels = 15, add_NA_factors = FALSE)
CustomerType_onehot<- predict(encoder, CustomerType, stringsAsFactors=TRUE)
raw_data_encoded <- cbind(raw_data_encoded, CustomerType_onehot)

NULL to NUMERIC

  • Agent
  • Company
# Agent
raw_data_encoded$Agent <- ifelse(raw_data_encoded$Agent == "NULL", 0,raw_data_encoded$Agent)
raw_data_encoded$Agent <- as.numeric(raw_data_encoded$Agent)
# Company
raw_data_encoded$Company <- ifelse(raw_data_encoded$Company == "NULL", 0,raw_data_encoded$Company)
raw_data_encoded$Company <- as.numeric(raw_data_encoded$Company)

AGENT YES

raw_data_encoded$AgentYes <- ifelse(raw_data_encoded$Agent == 0, 0, 1)

COMPANY YES

raw_data_encoded$CompanyYes <- ifelse(raw_data_encoded$Company == 0, 0, 1)

PRE-ENCODED AND COMMON SENSE COLUMNS DELETION

timeSeriesDataSet = raw_data_encoded
Error: object 'raw_data_encoded' not found

SAMPLING

set.seed(123)
#Encoded data sample
raw_data_encoded_sample = sample_n(raw_data_encoded, size = 1000)
Error in sample_n(raw_data_encoded, size = 1000) : 
  object 'raw_data_encoded' not found

TARGET VARIABLE

#isCanceled
isCanceled_sample = raw_data_encoded_sample[,1]
target <- as.data.frame(raw_data_encoded_sample %>%group_by(IsCanceled)%>%summarise(counts = n()))%>%mutate(perc = counts/nrow(raw_data_encoded_sample))

ggplot(target, aes(x = IsCanceled, y = perc)) + geom_bar(stat = "identity")+
  geom_text(aes(label = round(perc,2)))

PREDICTIVE VARIABLES

#Predictive Variables: all of them except:
  # - IsCanceled (Target Variable) and ArrivalDateYear.
# We erase ArrivalDateYear because we won't our models to be time replicable.
predictiveVariables_sample = raw_data_encoded_sample[,c(-1,-3)]

PREDICTION DATASET

raw_data_prediction <- raw_data_encoded[-3]
Error: object 'raw_data_encoded' not found

EDA

Target Variable %x

target <- as.data.frame(raw_data %>%group_by(IsCanceled)%>%summarise(counts = n()))%>%mutate(perc = counts/nrow(raw_data))

ggplot(target, aes(x = IsCanceled, y = perc)) + geom_bar(stat = "identity")+
  geom_text(aes(label = round(perc,2)))

Relationships

General Histogram Histogram based on type 0-1 Boxplot

#Plot function
eda_plot <- function(df_function, character, column){
  df_function <- df_function[,c(1,column)]
  colnames(df_function) <- c("IsCanceled", "Variable" )
  #############
  # HISTOGRAMS#
  #############
  bxp <- ggplot(df_function) +
    geom_density(aes(x = Variable, fill = IsCanceled), alpha = 0.2)+
    xlab(character)

  dp <- ggplot2.histogram(data=df_function, xName= 'Variable', addMeanLine=TRUE, meanLineColor="green",
                    meanLineType="dashed", meanLineSize=1,
                    addDensityCurve=TRUE, densityFill='blue', fill = 'blue')+ xlab(character)

  ##################################
  # RELATIONSHIPS BETWEEN VARIABLES########################################
  ##################################
 bp <- df_function %>%
   mutate(class = fct_reorder(IsCanceled,Variable, .fun='length' )) %>%
   ggplot( aes(x=IsCanceled, y= Variable, fill=class)) +
     geom_boxplot() +
     xlab("IsCanceled") +
     ylab(character)+
     theme(legend.position="none") +
     xlab("") +
     xlab("")

  ggarrange(bxp, dp, bp ,
          labels = c("A", "B", "C"),
          ncol = 2, nrow = 2)

}

raw_data$IsCanceled <- factor(raw_data$IsCanceled)
#Plot function
eda_plot_categorical <- function(df_function, character, column){
  df_function <- df_function[,c(1,column)]
  colnames(df_function) <- c("IsCanceled", "Variable" )
  #############
  # HISTOGRAMS#
  #############
  bxp <- ggplot(df_function) +
    geom_density(aes(x = Variable, fill = IsCanceled), alpha = 0.2)+
    xlab(character)

  dp <- ggplot2.histogram(data=df_function, xName= 'Variable', addMeanLine=TRUE, meanLineColor="green",
                    meanLineType="dashed", meanLineSize=1,
                    addDensityCurve=TRUE, densityFill='blue', fill = 'blue')+ xlab(character)



ggarrange(bxp, dp,
          labels = c("A", "B"),
          ncol = 2)
}
#Plot function
eda_plot_categorical <- function(df_function, character, column){
  df_function <- df_function[,c(1,column)]
  colnames(df_function) <- c("IsCanceled", "Variable" )
  #############
  # HISTOGRAMS#
  #############
  bxp <- ggplot(df_function) +
    geom_density(aes(x = Variable, fill = IsCanceled), alpha = 0.2)+
    xlab(character)
  bxp
}
eda_plot(raw_data, "LeadTime", column = 2)
eda_plot(raw_data, "ArrivalDateYear", column = 3)
eda_plot_categorical(raw_data, "ArrivalDateMonth", column = 4)
eda_plot(raw_data, "ArrivalDateWeekNumber", column = 5)
eda_plot(raw_data, "ArrivalDateDayOfMonth", column = 6)
eda_plot(raw_data, "StaysInWeekendNights", column = 7)
eda_plot(raw_data, "StaysInWeekNights", column = 8)
eda_plot(raw_data, "Adults", column = 9)
eda_plot(raw_data, "Children", column = 10)
eda_plot(raw_data, "Babies", column = 11)
eda_plot_categorical(raw_data, "Meal", column = 12)
eda_plot_categorical(raw_data, "Country", column = 13)
eda_plot_categorical(raw_data, "MarketSegment", column = 14)
eda_plot_categorical(raw_data, "DistributionChannel", column = 15)
eda_plot(raw_data, "IsRepeatedGuest", column = 16)
eda_plot(raw_data, "PreviousCancellations", column = 17)
eda_plot(raw_data, "PreviousBookingsNotCanceled", column = 18)
eda_plot_categorical(raw_data, "ReservedRoomType", column = 19)
eda_plot_categorical(raw_data, "AssignedRoomType", column = 20)
eda_plot(raw_data, "BookingChanges", column = 21)
eda_plot_categorical(raw_data, "DepositType", column = 22)

eda_plot_categorical(raw_data, "Agent", column = 23)
eda_plot_categorical(raw_data, "Company", column = 24)
eda_plot_categorical(raw_data, "DaysInWaitingList", column = 25)
eda_plot_categorical(raw_data, "CustomerType", column = 26)
eda_plot(raw_data, "ADR", column = 27)
eda_plot(raw_data, "RequiredCarParkingSpaces", column = 28)
eda_plot(raw_data, "TotalOfSpecialRequests", column = 29)
eda_plot_categorical(raw_data, "ReservationStatus", column = 30)
raw_data_encoded$IsCanceled <- factor(raw_data_encoded$IsCanceled)
eda_plot_categorical(raw_data_encoded, "AgentYes", column = 191)
eda_plot_categorical(raw_data_encoded, "CompanyYes", column = 192)

Matrix Correlation

  • Numeric and Ordinal Encoded Variables (from column 2 to 21)

corrplot(cor(raw_data_encoded[,2:21],
             use = "complete.obs"),
         method = "circle",
         type = 'upper',
         order = "hclust",
         addrect = 2,
         tl.cex = 0.4)

Specific to Prediction

Scatter Plots - Linear - Non Linear relationship

chart.Correlation(predictiveVariables_sample[,1:19],
                  histogram = TRUE, pch = 19)

Correlation Matrix with IsCanceled

highcorrelation <- raw_data_encoded
highcorrelation$IsCanceled <- as.numeric(highcorrelation$IsCanceled)

corrplot(cor(highcorrelation[,1:21],
             use = "complete.obs"),
         method = "circle",
         type = 'upper',
         order = "hclust",
         addrect = 2,
         tl.cex = 0.4)

Highest Linear Correlation with IsCanceled

#Check initial correlation between variables and IsCanceled based on a coefficient.

newData.cor = cor(highcorrelation[,1:21],
             use = "complete.obs")
corrplot(newData.cor)
corrplot(newData.cor, type = "upper", order = "hclust", tl.col = "black", tl.srt = 45)



newData.cor <- data.frame(newData.cor)
correlated <- c()
variable <- c()
coefficient_limit  <- c()
select_columns <-  c()
coeff <-  0.1


for(column in 1:dim(newData.cor)[1]){
  if(abs(newData.cor[1,column]) > coeff){
    variable <-  append(variable, names(newData.cor)[column])
    correlated  <-  append(correlated, newData.cor[1,column] )
    coefficient_limit <- append(coefficient_limit,coeff)
  }
}
correlated_variables <-  data.frame(variable,correlated, coefficient_limit)
# correlated_variables <-  data.frame(variable,correlated)

print(correlated_variables)

Contingency Table of Categorical Variables

#############################################
# CONTINGENCY TABLE OF CATEGORICAL VARIABLES#
#############################################

# IsCanceled VS ArrivalDateMonth#
#################################
## two-way contingency table of categorical outcome and predictors

contingency_table <- xtabs(~IsCanceled+ArrivalDateMonth, data = raw_data)
contingency_table_percentage <- c()
ArrivalDateMonth <- 0
for( ArrivalDateMonth in 1:dim(contingency_table)[2]){
  contingency_table_percentage[ArrivalDateMonth] = contingency_table[2, ArrivalDateMonth]/contingency_table[1, ArrivalDateMonth]*100
}

barplot(contingency_table_percentage,
        main = "IsCanceled % vs ArrivalDateMonth",
        xlab = "ArrivalDateMonth",
        ylab = "IsCanceled %",
        names.arg = c("April", "August","December","February","January", "July","June", "March", "May", "November",  "October", "September"),
        col = "darkred",
        horiz = FALSE)

default_total <- sum(contingency_table[2,])
# IsCanceled VS Meal#
#################################
## two-way contingency table of categorical outcome and predictors

contingency_table <- xtabs(~IsCanceled+Meal, data = raw_data)
contingency_table_percentage <- c()
Meal <- 0
for( Meal in 1:dim(contingency_table)[2]){
  contingency_table_percentage[Meal] = contingency_table[2, Meal]/contingency_table[1, Meal]*100
}

barplot(contingency_table_percentage,
        main = "IsCanceled % vs Meal",
        xlab = "Meal",
        ylab = "IsCanceled %",
        names.arg = c("BB", "FB", "HB", "SC", "Undefined"),
        col = "darkred",
        horiz = FALSE)

default_total <- sum(contingency_table[2,])
# IsCanceled VS MarketSegment#
#################################
## two-way contingency table of categorical outcome and predictors

contingency_table <- xtabs(~IsCanceled+MarketSegment, data = raw_data)
contingency_table_percentage <- c()
MarketSegment <- 0
for( MarketSegment in 1:dim(contingency_table)[2]){
  contingency_table_percentage[MarketSegment] = contingency_table[2, MarketSegment]/contingency_table[1, MarketSegment]*100
}

barplot(contingency_table_percentage,
        main = "IsCanceled % vs MarketSegment",
        xlab = "MarketSegment",
        ylab = "IsCanceled %",
        names.arg = c("Complementary", "Corporate","Direct","Groups", "Offline TA/TO", "Online TA"),
        col = "darkred",
        horiz = FALSE)

default_total <- sum(contingency_table[2,])
# IsCanceled VS DistributionChannel#
#################################
## two-way contingency table of categorical outcome and predictors

contingency_table <- xtabs(~IsCanceled+DistributionChannel, data = raw_data)
contingency_table_percentage <- c()
DistributionChannel <- 0
for( DistributionChannel in 1:dim(contingency_table)[2]){
  contingency_table_percentage[DistributionChannel] = contingency_table[2, DistributionChannel]/contingency_table[1, DistributionChannel]*100
}

barplot(contingency_table_percentage,
        main = "IsCanceled % vs DistributionChannel",
        xlab = "DistributionChannel",
        ylab = "IsCanceled %",
        names.arg = c("Corporate", "Direct", "TA/TO", "Undefined"),
        col = "darkred",
        horiz = FALSE)

default_total <- sum(contingency_table[2,])
# IsCanceled VS ReservedRoomType#
#################################
## two-way contingency table of categorical outcome and predictors

contingency_table <- xtabs(~IsCanceled+ReservedRoomType, data = raw_data)
contingency_table_percentage <- c()
ReservedRoomType <- 0
for( ReservedRoomType in 1:dim(contingency_table)[2]){
  contingency_table_percentage[ReservedRoomType] = contingency_table[2, ReservedRoomType]/contingency_table[1, ReservedRoomType]*100
}
contingency_table

barplot(contingency_table_percentage[-10],
        main = "IsCanceled % vs ReservedRoomType",
        xlab = "ReservedRoomType",
        ylab = "IsCanceled %",
        names.arg = c("A", "B", "C", "D","E","F","G","H","L"),
        col = "darkred",
        horiz = FALSE)
default_total <- sum(contingency_table[2,])
# IsCanceled VS AssignedRoomType#
#################################
## two-way contingency table of categorical outcome and predictors

contingency_table <- xtabs(~IsCanceled+AssignedRoomType, data = raw_data)
contingency_table_percentage <- c()
AssignedRoomType <- 0
for( AssignedRoomType in 1:dim(contingency_table)[2]){
  contingency_table_percentage[AssignedRoomType] = contingency_table[2, AssignedRoomType]/contingency_table[1, AssignedRoomType]*100
}

barplot(contingency_table_percentage[-c(10,11)],
        main = "IsCanceled % vs AssignedRoomType",
        xlab = "AssignedRoomType",
        ylab = "IsCanceled %",
        names.arg = c("A", "B", "C", "D","E","F","G","H","I"),
        col = "darkred",
        horiz = FALSE)
default_total <- sum(contingency_table[2,])
# IsCanceled VS DepositType#
#################################
## two-way contingency table of categorical outcome and predictors

contingency_table <- xtabs(~IsCanceled+DepositType, data = raw_data)
contingency_table_percentage <- c()
DepositType <- 0
for( DepositType in 1:dim(contingency_table)[2]){
  contingency_table_percentage[DepositType] = contingency_table[2, DepositType]/contingency_table[1, DepositType]*100
}

barplot(contingency_table_percentage,
        main = "IsCanceled % vs DepositType",
        xlab = "DepositType",
        ylab = "IsCanceled %",
        names.arg = c("No Deposit", "Non Refund", "Refundable"),
        col = "darkred",
        horiz = FALSE)
default_total <- sum(contingency_table[2,])
# IsCanceled VS CustomerType#
#################################
## two-way contingency table of categorical outcome and predictors

contingency_table <- xtabs(~IsCanceled+CustomerType, data = raw_data)
contingency_table_percentage <- c()
CustomerType <- 0
for( CustomerType in 1:dim(contingency_table)[2]){
  contingency_table_percentage[CustomerType] = contingency_table[2, CustomerType]/contingency_table[1, CustomerType]*100
}

barplot(contingency_table_percentage,
        main = "IsCanceled % vs CustomerType",
        xlab = "CustomerType",
        ylab = "IsCanceled %",
        names.arg = c("Contract", "Group", "Transient", "Transient-Party"),
        col = "darkred",
        horiz = FALSE)
default_total <- sum(contingency_table[2,])
# IsCanceled VS CompanyYes#
#################################
## two-way contingency table of categorical outcome and predictors

contingency_table <- xtabs(~IsCanceled+CompanyYes, data = raw_data_encoded)
contingency_table_percentage <- c()
CompanyYes <- 0
for( CompanyYes in 1:dim(contingency_table)[2]){
  contingency_table_percentage[CompanyYes] = contingency_table[2, CompanyYes]/contingency_table[1, CompanyYes]*100
}

barplot(contingency_table_percentage,
        main = "IsCanceled % vs CompanyYes",
        xlab = "CompanyYes",
        ylab = "IsCanceled %",
        names.arg = c(0, 1),
        col = "darkred",
        horiz = FALSE)
default_total <- sum(contingency_table[2,])
# IsCanceled VS AgentYes#
#################################
## two-way contingency table of categorical outcome and predictors

contingency_table <- xtabs(~IsCanceled+AgentYes, data = raw_data_encoded)
contingency_table_percentage <- c()
AgentYes <- 0
for( AgentYes in 1:dim(contingency_table)[2]){
  contingency_table_percentage[AgentYes] = contingency_table[2, AgentYes]/contingency_table[1, AgentYes]*100
}

barplot(contingency_table_percentage,
        main = "IsCanceled % vs AgentYes",
        xlab = "AgentYes",
        ylab = "IsCanceled %",
        names.arg = c(0, 1),
        col = "darkred",
        horiz = FALSE)
default_total <- sum(contingency_table[2,])

CLUSTERING

Clustering: - Goodness of the cluster Analysis - Optimal Number of Clusters Clustering

DATA PRE-PROCESSING

SCALING

#Scaled sample
predictiveVariables_sampleScaled = scale(predictiveVariables_sample)
predictiveVariables_sampleScaled = as.data.frame(predictiveVariables_sampleScaled)

VARIABLES WITH ZERO VALUES IN ALL COLUMNS

#Columns with all zeros from the sample
predictiveVariablesNonZero = predictiveVariables_sample[!sapply(predictiveVariables_sample, function(x) sum(x)== 0)]
predictiveVariablesNonZeroScaled = scale(predictiveVariablesNonZero)

GOWER

library(cluster)
gower_dist = daisy(as.matrix(predictiveVariables_sample) , metric = "euclidean", stand = FALSE)

GOODNESS OF THE CLUSTER ANALYSIS

bondad_ac = get_clust_tendency(predictiveVariables_sample, 500)
bondad_ac$hopkins_stat

OPTIMAL NUMBER OF CLUSTERS

NUMBER FOR NON HIERARCHICAL CLUSTERING

GENERAL FOR ALL OF THEM

clus.nb = NbClust(predictiveVariablesNonZeroScaled, distance = "euclidean",min.nc = 2, max.nc = 10,method = "complete", index ="gap")

clus.nb$Best.nc

PAM

# Calculate silhouette width for many k using PAM
sil_width <- c(NA)
for(i in 2:10){
  pam_fit <- pam(gower_dist,
                 diss = TRUE,
                 k = i)
  sil_width[i] <- pam_fit$silinfo$avg.width
}
# Plot sihouette width (higher is better)
plot(1:10, sil_width,
     xlab = "Optimal k clusters",
     ylab = "Average Width Profile")
lines(1:10, sil_width)

CLARA

# Only numeric Variables for clustering
numericVariables <- predictiveVariablesNonZero[c(17,7,2,3,9,13,8,16,1,12,11,18,5,6,19)]
fviz_nbclust(numericVariables, cluster::clara, method = "silhouette") + ggtitle("Optimal number of clusters - CLARA") + labs(x = "Optimal k clusters", y = "Average Width Profile")
# 2 groups

The optimal number of clusters will be given by the value of k that maximizes the average profile. In this case: 2

FUZZY ANALYSIS CLUSTERING

# Calculate silhouette width for many k using 
sil_width <- c(NA)
for(i in 2:10){
 fanny_fit <- fanny(gower_dist,
                 diss = TRUE,
                 k = i)
  sil_width[i] <- fanny_fit$silinfo$avg.width
}
# Plot sihouette width (higher is better)
plot(1:10, sil_width,
     xlab = "Number of clusters",
     ylab = "Silhouette Width")
lines(1:10, sil_width)

NUMBER FOR HIERARCHICAL CLUSTERING

HCUT

aggl.clust.c <- hclust(gower_dist, method = "complete") 


library(fpc)
cstats.table <- function(dist, tree, k) {
  clust.assess <- c("cluster.number","n","within.cluster.ss","average.within","average.between","wb.ratio","dunn2","avg.silwidth")
  clust.size <- c("cluster.size")
  stats.names <- c()
  row.clust <- c()
  output.stats <- matrix(ncol = k, nrow = length(clust.assess))
  cluster.sizes <- matrix(ncol = k, nrow = k)
  for (i in c(1:k)) {
    row.clust[i] <- paste("Cluster-", i, " size")
  }
  for (i in c(2:k)) {
    stats.names[i] <- paste("Test", i - 1)
    
    for (j in seq_along(clust.assess)) {
      output.stats[j, i] <- unlist(cluster.stats(d = dist, clustering = cutree(tree, k = i))[clust.assess])[j]
      
    }
    
    for (d in 1:k) {
      cluster.sizes[d, i] <- unlist(cluster.stats(d = dist, clustering = cutree(tree, k = i))[clust.size])[d]
      dim(cluster.sizes[d, i]) <- c(length(cluster.sizes[i]), 1)
      cluster.sizes[d, i]
      
    }
  }
  output.stats.df <- data.frame(output.stats)
  cluster.sizes <- data.frame(cluster.sizes)
  cluster.sizes[is.na(cluster.sizes)] <- 0
  rows.all <- c(clust.assess, row.clust)
  
  # rownames(output.stats.df) <- clust.assess
  output <- rbind(output.stats.df, cluster.sizes)[ ,-1]
  colnames(output) <- stats.names[2:k]
  rownames(output) <- rows.all
  is.num <- sapply(output, is.numeric)
  output[is.num] <- lapply(output[is.num], round, 2)
  output
}
ggplot(data = data.frame(t(cstats.table(gower_dist, aggl.clust.c, 15))), 
       aes(x = cluster.number, y = avg.silwidth)) +
  geom_point() +
  geom_line() +
  ggtitle("Optimal number of clusters - HCUT") +
  labs(x = "Optimal k clusters", y = "Average Width Profile") +
  theme(plot.title = element_text(hjust = 0.5))

HIERARCHICAL CLUSTERING

DENDROGRAM

plot(aggl.clust.c, main = "HCUT ")
rect.hclust(aggl.clust.c, k = 2, border = 2:3)

CLUSTER PLOT

set.seed(123)
grp <- cutree(aggl.clust.c, k = 2)
fviz_cluster(list(data = predictiveVariables_sample, cluster = grp), stand = FALSE, geom = "point", pointsize = 1, title = "Cluster Plot")

CONTINGENCY TABLE


clust.num <- cutree(aggl.clust.c, k = 2)
tabl = prop.table(table(clust.num,isCanceled_sample))*100
tabl
tabl[1,2]/(tabl[1]+ tabl[1,2])*100 # % of Cancelations of Group 1
tabl[2,2]/(tabl[2]+tabl[2,2])*100 # % of Cancelations of Group 2

SILHOUETTE PLOT

grp <- cutree(aggl.clust.c, k = 2)
# aggl.clust.c <- hclust(gower_dist, method = "complete") 
sil_cl <- silhouette(grp,gower_dist, title=title(main = 'Good'))
# rownames(sil_cl) <- rownames(grp)
plot(sil_cl)

NON- HIERARCHICAL CLUSTERING

PAM

CLUSTER PLOT

library(Rtsne)
pam_fit <- pam(gower_dist, diss = TRUE, k = 4)

tsne_obj <- Rtsne(gower_dist, is_distance = TRUE)
tsne_data <- tsne_obj$Y %>%
  data.frame() %>%
  setNames(c("X", "Y")) %>%
  mutate(cluster = factor(pam_fit$clustering))
ggplot(aes(x = X, y = Y), data = tsne_data) +
  geom_point(aes(color = cluster))

CONTINGENCY TABLE

pam_fit$clustering<- as.factor(pam_fit$clustering)
tabl = prop.table(table(pam_fit$clustering,isCanceled_sample))*100
tabl
tabl[1,2]/(tabl[1]+ tabl[1,2])*100 # % of Cancelations of Group 1
tabl[2,2]/(tabl[2]+tabl[2,2])*100 # % of Cancelations of Group 2
tabl[3,2]/(tabl[3]+tabl[3,2])*100 # % of Cancelations of Group 3
tabl[4,2]/(tabl[4]+tabl[4,2])*100 # % of Cancelations of Group 4
# tabl[5,2]/(tabl[5]+tabl[5,2])*100 # % of Cancelations of Group 5
# tabl[6,2]/(tabl[6]+tabl[6,2])*100 # % of Cancelations of Group 6
# tabl[7,2]/(tabl[7]+tabl[7,2])*100 # % of Cancelations of Group 7
# tabl[8,2]/(tabl[8]+tabl[8,2])*100 # % of Cancelations of Group 8
# tabl[9,2]/(tabl[9]+tabl[9,2])*100 # % of Cancelations of Group 9
# tabl[10,2]/(tabl[10]+tabl[10,2])*100 # % of Cancelations of Group 10

SILHOUETTE PLOT

fviz_silhouette(pam_fit)

CLARA

CLUSTER PLOT

library(cluster)
claraC2 = clara(numericVariables, 2,
                    samples = 1000, correct.d = FALSE)
fviz_cluster(claraC2, geom = "point", pointsize = 1, ellipse.type = "norm")

CONTINGENCY TABLE

claraC2$clustering <- as.factor(claraC2$clustering)
tabl = prop.table(table(claraC2$clustering,isCanceled_sample))*100
tabl
tabl[1,2]/(tabl[1] + tabl[1,2])*100 # % of Cancelations of Group 1
tabl[2,2]/(tabl[2] + tabl[2,2])*100 # % of Cancelations of Group 2

SILHOUETTE PLOT

fviz_silhouette(claraC2)

FUZZY ANALYSIS CLUSTERING

CLUSTER PLOT

library(Rtsne)
fanny_fit <- fanny(gower_dist, diss = TRUE, k = 2)

tsne_obj <- Rtsne(gower_dist, is_distance = TRUE)
tsne_data <- tsne_obj$Y %>%
  data.frame() %>%
  setNames(c("X", "Y")) %>%
  mutate(cluster = factor(fanny_fit$clustering))
ggplot(aes(x = X, y = Y), data = tsne_data) +
  geom_point(aes(color = cluster))
# Coeficiente de segmentación normalizado:
fanny_fit$coeff

CONTINGENCY TABLE

fanny_fit$clustering <- as.factor(fanny_fit$clustering)
tabl = prop.table(table(fanny_fit$clustering,isCanceled_sample))*100
tabl
tabl[1,2]/(tabl[1]+ tabl[1,2])*100 # % of Cancelations of Group 1
tabl[2,2]/(tabl[2]+tabl[2,2])*100 # % of Cancelations of Group 2

SILHOUETTE PLOT

fviz_silhouette(fanny_fit)

DIMENSION REDUCTION

CORRESPONDENCE ANALYSIS

RESERVED ROOM VS DISTRIBUTION CHANNEL

roomChannel = as.data.frame.matrix(table(raw_data$DistributionChannel, raw_data$ReservedRoomType))
roomChannel

as.table(as.matrix(roomChannel))
roomChannelScaled = scale(roomChannel)
roomChannelScaled

Independence Test

test.ind = chisq.test(roomChannel)
test.ind

# The result of the test confirms the possibility of rejecting the hypotheses of independence between categories of rows and columns, or, what is the same, we can affirm that there is a relationship between them. The rest of the tests presented below allow us to help visualize and interpret the test result.

Analysis

roomChannelt = as.table(as.matrix(roomChannel))
roomChannelt
library("FactoMineR")
# tab1 <- as.data.frame.matrix(table(as.factor(df$X1),as.factor(df$X2)))
# res.ca <- CA(tab1, graph = FALSE)
library(FactoMineR)
roomChannel.ca=CA(roomChannel, graph = FALSE)
summary(roomChannel.ca)
fviz_ca_biplot(roomChannel.ca, map ="rowprincipal", arrow = c(TRUE, TRUE))
fviz_ca_biplot(roomChannel.ca, map ="colgreen",
               arrow = c(TRUE, FALSE))+
        ggtitle("Contribution of Distribution Channels to the dimensions")
fviz_ca_biplot(roomChannel.ca, map ="rowgreen",
               arrow = c(FALSE, TRUE))+
        ggtitle("Contribution of Room Types to the dimensions")

COUNTRY VS RESERVED ROOM

countryRoom = as.data.frame.matrix(table(raw_data$Country, raw_data$ReservedRoomType))
countryRoom

Independence Test

test.ind = chisq.test(countryRoom)
test.ind

# The result of the test confirms the possibility of rejecting the hypotheses of independence between categories of rows and columns, or, what is the same, we can affirm that there is a relationship between them. The rest of the tests presented below allow us to help visualize and interpret the test result.

Analysis

library(FactoMineR)
countryRoom.ca=CA(countryRoom, graph = FALSE)
summary(countryRoom.ca)
fviz_ca_biplot(countryRoom.ca, map ="rowprincipal", arrow = c(TRUE, TRUE))
fviz_ca_biplot(countryRoom.ca, map ="colgreen",
               arrow = c(TRUE, FALSE))+
        ggtitle("Contribution of Country to the dimensions")
fviz_ca_biplot(countryRoom.ca, map ="rowgreen",
               arrow = c(FALSE, TRUE))+
        ggtitle("Contribution of Room Type to the dimensions")

PCA

# Only numeric Variables for clustering
numericVariables <- predictiveVariablesNonZero[c(17,7,2,3,9,13,8,16,1,12,11,18,5,6,19)]
normalizeData <- function(x){
  m <- mean(x)
  s <- sd(x)
  x <- (x - m)/s
}

numericVariables <- apply(numericVariables, 2, normalizeData)

EXPLORATORY FACTOR ANALYSIS

In order to apply these unsupervised learning techniques, i.e. PCA, our data set must have some properties:

Determinant of the correlation matrix

The determinant of our correlation matrix is 0.04720488. As we have a low determinant near to 0, it makes sense to apply PCA to reduce dimensions, because it also tells us that there is a high correlation between the variables studied.

KMO

For reference, Kaiser put the following values on the results: 0.00 to 0.49 unacceptable. 0.50 to 0.59 miserable. 0.60 to 0.69 mediocre. 0.70 to 0.79 middling. 0.80 to 0.89 meritorious. 0.90 to 1.00 marvelous.

So in our case, our variables are meritorious to apply factor analysis as our Overall MSA = 0.8.

Bartlett’s Test of Sphericity

In our case the p-value is equal to 0, so the factor analysis will be useful with our data as we are accepting the alternative hypothesis that our variables are correlated enough and diverging from the identity matrix.

Conclusions of the data exploratory analysis

After doing all these tests we can apply a data reduction technique such as the Principal Component Analysis with confidence.

PCA

A dimensionality reduction technique such as a PCA makes sense to see what variables can be explained in the same dimension.

As we can see in Individuals factor map, the directions of Dim1 and Dim2 are clearly orthogonal and explain 10.4% of the variance:

  • 6% of the variance can be explained in Dim1.

  • 4.4% of the variance can be explained in Dim2.

If the first two factors together explain most of the variability in the original 9 variables, then those factors are clearly a good, simpler substitute for all 9 variables. We can drop the rest without losing much of the original variability.

But if it takes 7 factors to explain most of the variance in those 9 variables, we might as well just use the original 9.

In this case we probably need more than 2 dimensions to explain most of the variance as we are only explaining 53% of the variance.

Loadings/Coordinates

Here we start seeing that with more dimensions we are able to explain a higher percentage of our dataset but we still don’t see a clear difference between what the different dimensions explain.

cos2: quality of the representation for variables on the factor map

Scree Plot

Contributions

All variables contribute to Dim1 or Dim2 above the threshold. If the contribution of the variables were uniform, the expected value would be 1/length(variables)

So this is the expected average contribution that we can use as a threshold.

Using that as a threshold, here we can see the highest contributions to Dim1:

Using that as a threshold, here we can see the highest contributions to Dim2: length, lbh, wskull, ltibio.

Eigenvalues

The variance percentage and cumulative variance percentage changes less once we get to the fourth or fifth dimension.

El segundo documento, de extensión máxima 6 pp, resolverá un problema de predicción siendo la variable objetivo predecir si la reserva se cancela o no, utilizando las técnicas de regresión estudiadas en la asignatura.

#PREDICTION

LOGISTIC REGRESSION

Logistic regression is a method for fitting a regression curve, y = f(x), when y is a categorical variable. The typical use of this model is predicting y given a set of predictors x. The predictors can be continuous, categorical or a mix of both. - PREDICTIVE MODEL: Multicolineality is not relevant as we are not searching for an explanatory model. ### TRAIN AND TEST

#TRAINING AND TESTING DATA
set.seed(131822)
n <- nrow(raw_data_prediction)
scaleDF  <- scale(raw_data_prediction)
id_train <- sample(1:n , 0.80*n)
df.train <- as.data.frame(scaleDF[id_train,])
df.train$IsCanceled <- raw_data[id_train,]$IsCanceled
df.test  <- as.data.frame(scaleDF[-id_train,])
df.test$IsCanceled <- raw_data[-id_train,]$IsCanceled

Train Target Variable %

target <- as.data.frame(df.train %>% group_by(IsCanceled) %>% summarise(counts = n())) %>% mutate(perc = counts/nrow(df.train))
ggplot(target, aes(x = IsCanceled, y = perc)) + geom_bar(stat = "identity") +
  geom_text(aes(label = round(perc,2)))

Test Target Variable %

target <- as.data.frame(df.test %>% group_by(IsCanceled) %>% summarise(counts = n())) %>% mutate(perc = counts/nrow(df.test))
ggplot(target, aes(x = IsCanceled, y = perc)) + geom_bar(stat = "identity") +
  geom_text(aes(label = round(perc,2)))

Logistic Regression

#######################
# COST IN TRAINING SET#
#######################
searchgrid = seq(0.0001,0.6, 0.01)
result = cbind(searchgrid, NA)
# in the cost function, both r and pi are vectors, r=truth, pi=predicted probability
cost1 <- function(r, pi){
  weight1 <- 1
  weight0 <- 1
  c1 <- (r == 1) & (pi < pcut) # true if actual 1 but predict 0. FP (False Positive)
  c0 <- (r == 0) & (pi > pcut) #true if actual 0 but predict 1. FN (False Negative)
  return(mean(weight1 * c1 + weight0 * c0))
}

Train and Test sets for Regularization

## Train
datos_train_x <- model.matrix(IsCanceled ~ ., df.train)[, -1]
datos_train_y <- df.train$IsCanceled
## test
datos_test_x <- model.matrix(IsCanceled ~ ., df.test)[, -1]
datos_test_y <- df.test$IsCanceled

Logistic Regression model

df.glm1 <- glm(IsCanceled ~ ., family = binomial, df.train)
glm.fit: algorithm did not convergeglm.fit: fitted probabilities numerically 0 or 1 occurred
prob <- predict(df.glm1,type = "response")
for (i in 1:length(searchgrid))
{
  pcut <- result[i,1]
  #assign the cost to the 2nd col
  result[i,2] <- cost1(df.train$IsCanceled, prob)
}
plot(result, ylab = "Cost in Training Set")

result[which.min(result[,2]),]
searchgrid            
 0.4401000  0.1512731 
# searchgrid            
# 0.4341000  0.1508362

PREDICTION TRAIN

#######################
cut_off <- 0.4401000#
#######################
prob.glm1.insample <- predict(df.glm1,type = "response")
predicted.glm1.insample <- (prob.glm1.insample > cut_off)
predicted.glm1.insample <- as.numeric(predicted.glm1.insample)
###################
# CONFUSION MATRIX#
###################
table(df.train$IsCanceled, predicted.glm1.insample, dnn = c("Truth","Predicted"))
     Predicted
Truth     0     1
    0 21072  2128
    1  2720  6128
########
# ERROR#
########
mean(ifelse(df.train$IsCanceled != predicted.glm1.insample, 1, 0))
[1] 0.1512731
mean(ifelse(df.train$IsCanceled == predicted.glm1.insample, 1, 0))
[1] 0.8487269

PREDICTION TEST

#######################
cut_off <- 0.4401000#
#######################
prob.glm1.outsample <- predict(df.glm1, type = "response",newdata = df.test)
prediction from a rank-deficient fit may be misleading
predicted.glm1.outsample <- (prob.glm1.outsample > cut_off)
predicted.glm1.outsample <- as.numeric(predicted.glm1.outsample)
###################
# CONFUSION MATRIX#
###################
table(df.test$IsCanceled, predicted.glm1.outsample, dnn = c("Truth","Predicted"))
     Predicted
Truth    0    1
    0 5221  517
    1  689 1585
########
# ERROR#
########
mean(ifelse(df.test$IsCanceled != predicted.glm1.outsample, 1, 0))
[1] 0.1505242
mean(ifelse(df.train$IsCanceled == predicted.glm1.insample, 1, 0))
[1] 0.8487269

ROC CURVE

Our Area Under the Curve is ROC curve, is a graphical plot that illustrates the diagnostic ability of a binary classifier system as its discrimination threshold is varied. It quantifies the tradeoff we’re making between the TPR (True Positive Rate) and the false positive rate (FPR) at various cutoff settings ( between 0 and 1 ). As TPR increases, FPR increases too, so we want to reach the highest TPR before the FPR increases too much. This means that we want our AUC to be really close to 1.

library('ROCR')
############
# ROC CURVE#####################################################################################################
############
pred <- prediction(prob.glm1.outsample, df.test$IsCanceled)
perf <- performance(pred, "tpr", "fpr")
rocr.auc.lr = as.numeric(performance(pred, "auc")@y.values)
plot(perf, colorize = TRUE,
      main = 'ROC Curve')
mtext(paste('Logistic Regression - auc : ', round(rocr.auc.lr, 5)))

#Get the AUC
unlist(slot(performance(pred, "auc"), "y.values"))
[1] 0.9110479
# 
####################################################################
# ODDS OF DEFAULT BASED ON WEIGHTS(COEFFICIENTS) GIVEN TO VARIABLES#
####################################################################
# How odds of default change by changing one unit of the variable. 
temp_compare <- as.data.frame(exp(cbind(OR = coef(df.glm1))))
temp_compare
library(data.table)
setDT(temp_compare, keep.rownames = TRUE)[]
temp_compare <- temp_compare[order(temp_compare$OR,decreasing = TRUE),]
temp_compare <- temp_compare[!is.infinite(temp_compare$OR)]
temp_compare <- temp_compare[!is.na(temp_compare$OR)]

Lasso for Feature Selection

library(glmnet)
df.Lasso <- glmnet(datos_train_x, as.factor(datos_train_y), alpha = 1.0, family = "binomia")
prob <- predict(df.Lasso, newx = datos_train_x, type = "response")
res <- c()
for (j in 1:ncol(prob)){
  for (i in 1:length(searchgrid))
  {
    pcut <- result[i,1]
    #assign the cost to the 2nd col
    result[i,2] <- cost1(df.train$IsCanceled, prob[,j])
  }
##plot(result, ylab = "Cost in Training Set")
res <- c(res,as.double(result[which.min(result[,2]),][2]))
}
which(res == min(res))[1]
[1] 98
result = cbind(searchgrid, NA)
for (i in 1:length(searchgrid))
  {
    pcut <- result[i,1]
    #assign the cost to the 2nd col (which(res == min(res))[1])
    result[i,2] <- cost1(df.train$IsCanceled, prob[,98])
  }
plot(result, ylab = "Cost in Training Set")

result[which.min(result[,2]),]
searchgrid            
 0.4301000  0.1511483 
# searchgrid            
#  0.4301000  0.1511483

PREDICTION TRAIN

#######################
cut_off <- 0.4301000#
#######################
prob.Lasso <-  as.double(prob[,98])
prob.Lasso <- (prob.Lasso > cut_off)
prob.Lasso <- as.numeric(prob.Lasso)
###################
# CONFUSION MATRIX#
###################
table(df.train$IsCanceled, prob.Lasso, dnn = c("Truth","Predicted"))
     Predicted
Truth     0     1
    0 20936  2264
    1  2580  6268
########
# ERROR#
########
mean(ifelse(df.train$IsCanceled != prob.Lasso, 1, 0))
[1] 0.1511483

PREDICTION TEST

#######################
cut_off <- 0.4301000#
#######################
prob <- predict(df.Lasso, newx = datos_test_x, type = "response")
prob.Lasso <-  as.double(prob[,98])
prob.Lasso <- (prob.Lasso > cut_off)
prob.Lasso <- as.numeric(prob.Lasso)
###################
# CONFUSION MATRIX#
###################
table(df.test$IsCanceled, prob.Lasso, dnn = c("Truth","Predicted"))
     Predicted
Truth    0    1
    0 5185  553
    1  645 1629
########
# ERROR#
########
mean(ifelse(df.test$IsCanceled != prob.Lasso, 1, 0))
[1] 0.1495257
mean(ifelse(df.train$IsCanceled == prob.Lasso, 1, 0))
[1] 0.6002247

ROC CURVE

Our Area Under the Curve is ROC curve, is a graphical plot that illustrates the diagnostic ability of a binary classifier system as its discrimination threshold is varied. It quantifies the tradeoff we’re making between the TPR (True Positive Rate) and the false positive rate (FPR) at various cutoff settings ( between 0 and 1 ). As TPR increases, FPR increases too, so we want to reach the highest TPR before the FPR increases too much. This means that we want our AUC to be really close to 1.

############
# ROC CURVE#####################################################################################################
############
pred <- prediction(prob.Lasso, df.test$IsCanceled)
perf <- performance(pred, "tpr", "fpr")
rocr.auc.lr = as.numeric(performance(pred, "auc")@y.values)
plot(perf, colorize = TRUE,
      main = 'ROC Curve')
mtext(paste('Logistic Regression - auc : ', round(rocr.auc.lr, 5)))

#Get the AUC
unlist(slot(performance(pred, "auc"), "y.values"))
[1] 0.8099919
# 
####################################################################
# ODDS OF DEFAULT BASED ON WEIGHTS(COEFFICIENTS) GIVEN TO VARIABLES#
####################################################################
# How odds of default change by changing one unit of the variable.
temp_compareL <- as.data.frame((cbind(OR = coef(df.Lasso)[,10])))
temp_compareL
setDT(temp_compareL, keep.rownames = TRUE)[]
temp_compareL <- temp_compareL[order(temp_compareL$OR,decreasing = TRUE),]
temp_compareL <- temp_compareL[!is.infinite(temp_compareL$OR)]
temp_compareL <- temp_compareL[!is.na(temp_compareL$OR)]
temp_compareL

El tercer documento, de extensión máxima 6 pp, resolverá un problema de predicción de series temporales. En primer lugar, se debe elaborar la serie semanal de reservas realizadas. Se deberá plantear un modelo adecuado para predecir esta serie temporal.

TIME SERIES ANALYSIS

Time Series: HOTEL’S MANAGEMENT - Number of Reservations - ArrivalDate - Number of Reservations that will CheckOut - ArrivalDate - Number of Reservations that will not CheckOut - ArrivalDate

HOTEL’S MARKETING
- Number of Reservations that will CheckOut - ReservationDate - Number of Reservations that will not CheckOut- ReservationDate

DATA PRE-PROCESSING

  • We obtain ArrivalDate from different Columns.
erase_columns <- c(12, 13, 14, 15, 19, 20, 22, 26, 30)
timeSeriesDataSet <- timeSeriesDataSet[ -erase_columns ]

EXPLORATORY DATA ANALYSIS - TIME SERIES

TIME SERIES FIRST VIEW
Arrival Date
yearMonthDay <- function(y, m, d){
  date <- paste(as.character(y),paste(m, d, sep = "-"), sep = "-")
  date <- as.Date(date, "%Y-%m-%d")
}
arrival_date <- yearMonthDay(y = timeSeriesDataSet$ArrivalDateYear,
                             m = timeSeriesDataSet$ArrivalDateMonth,
                             d = timeSeriesDataSet$ArrivalDateDayOfMonth)

arrival_date <- as.data.frame(arrival_date) 

a = arrival_date %>% group_by(arrival_date) %>% summarise(n = n())
data <- as.xts(a$n,order.by=as.Date(a$arrival_date))
weekly <- apply.weekly(data,sum)
weekly <- as.data.frame(weekly)

#index of a dataframe to columns
library(data.table)
setDT(weekly, keep.rownames = TRUE)[]
weekly$rn <- as.Date(weekly$rn)
arrivalDatePlot <- weekly
ggplot(data = weekly,
      aes(x = rn))+
      geom_line(aes(y = V1, color = "Number of Reservations for ArrivalDate"))+
      ylab('Number of Reservations for that Arrival Date')+
      xlab('Date - Weekly')+
      theme(legend.position = "none")
Reservation Date
  • ReservationDate = ArrivalDate - LeadTime
yearMonthDay <- function(y, m, d){
  date <- paste(as.character(y),paste(m, d, sep = "-"), sep = "-")
  date <- as.Date(date, "%Y-%m-%d")
}
arrival_date <- yearMonthDay(y = timeSeriesDataSet$ArrivalDateYear,
                             m = timeSeriesDataSet$ArrivalDateMonth,
                             d = timeSeriesDataSet$ArrivalDateDayOfMonth)
reservationDate <- arrival_date - timeSeriesDataSet$LeadTime
reservationDate <- as.data.frame(reservationDate) 

# reservationDate <- reservationDate%>%rename( 'reservationDate' = 'arrival_date' )

         
a = reservationDate %>% group_by(reservationDate) %>% summarise(n = n())
data <- as.xts(a$n,order.by=as.Date(a$reservationDate))
weekly <- apply.weekly(data,sum)
weekly <- as.data.frame(weekly)

#index of a dataframe to columns
library(data.table)
setDT(weekly, keep.rownames = TRUE)[]
weekly$rn <- as.Date(weekly$rn)
reservationDatePlot <- weekly
ggplot(data = weekly,
      aes(x = rn))+
      geom_line(aes(y = V1, color = "Number of Reservations for Reservation Date"))+
      ylab('Number of Reservations for that Reservation Date')+
      xlab('Date - Weekly')+
      theme(legend.position = "none")

Arrival and Reservation Date
ggplot() +
  geom_line(data = arrivalDatePlot, aes(x = rn, y = V1, col = "Arrival Date")) + 
  geom_line(data = reservationDatePlot, aes(x = rn, y = V1, col = "Reservation Date"))+
  xlab('Date - Weeks')+
  ylab('Number of Reservations')+
  ggtitle('Number of Reservations')
nonCheckoutTS<-timeSeriesDataSet%>%filter(IsCanceled == 1)
checkoutTS <-timeSeriesDataSet%>%filter(IsCanceled == 0)
Arrival Date Checkout

arrival_dateCheckout <- yearMonthDay(y = checkoutTS$ArrivalDateYear,
                             m = checkoutTS$ArrivalDateMonth,
                             d = checkoutTS$ArrivalDateDayOfMonth)

arrival_dateCheckout <- as.data.frame(arrival_dateCheckout) 

a = arrival_dateCheckout %>% group_by(arrival_dateCheckout) %>% summarise(n = n())
data <- as.xts(a$n,order.by=as.Date(a$arrival_dateCheckout))
weekly <- apply.weekly(data,sum)
weekly <- as.data.frame(weekly)

#index of a dataframe to columns
library(data.table)
setDT(weekly, keep.rownames = TRUE)[]
weekly$rn <- as.Date(weekly$rn)
arrivalDatePlotCheckout <- weekly
ggplot(data = weekly,
      aes(x = rn))+
      geom_line(aes(y = V1, color = "Number of Reservations for ArrivalDate of people who Checkout"))+
      ylab('Number of Reservations for that Arrival Date of people who Checkout')+
      xlab('Date - Weekly')+
      theme(legend.position = "none")
Arrival Date Canceled

arrival_dateCanceled <- yearMonthDay(y = nonCheckoutTS$ArrivalDateYear,
                             m = nonCheckoutTS$ArrivalDateMonth,
                             d = nonCheckoutTS$ArrivalDateDayOfMonth)

arrival_dateCanceled <- as.data.frame(arrival_dateCanceled) 

a = arrival_dateCanceled %>% group_by(arrival_dateCanceled) %>% summarise(n = n())
data <- as.xts(a$n,order.by=as.Date(a$arrival_dateCanceled))
weekly <- apply.weekly(data,sum)
weekly <- as.data.frame(weekly)

#index of a dataframe to columns
library(data.table)
setDT(weekly, keep.rownames = TRUE)[]
weekly$rn <- as.Date(weekly$rn)
arrivalDatePlotCanceled <- weekly
ggplot(data = weekly,
      aes(x = rn))+
      geom_line(aes(y = V1, color = "Number of Reservations for ArrivalDate of people who Checkout"))+
      ylab('Number of Reservations for that Arrival Date of people who Checkout')+
      xlab('Date - Weekly')+
      theme(legend.position = "none")
Arrival Date Checkout and Canceled
ggplot() +
  geom_line(data = arrivalDatePlotCheckout, aes(x = rn, y = V1, col = "Arrival Date Checkout")) + 
  geom_line(data = arrivalDatePlotCanceled, aes(x = rn, y = V1, col = "Arrival Date Canceled"))+
  xlab('Date - Weeks')+
  ylab('Number of Reservations')+
  ggtitle('Number of Reservations')
Reservation Date Canceled
reservetion_dateNonCheckout <- yearMonthDay(y = nonCheckoutTS$ArrivalDateYear,
                             m = nonCheckoutTS$ArrivalDateMonth,
                             d = nonCheckoutTS$ArrivalDateDayOfMonth)
reservetion_dateNonCheckout <- reservetion_dateNonCheckout - nonCheckoutTS$LeadTime

reservetion_dateNonCheckout <- as.data.frame(reservetion_dateNonCheckout) 

a = reservetion_dateNonCheckout %>% group_by(reservetion_dateNonCheckout) %>% summarise(n = n())
data <- as.xts(a$n,order.by=as.Date(a$reservetion_dateNonCheckout))
weekly <- apply.weekly(data,sum)
weekly <- as.data.frame(weekly)

#index of a dataframe to columns
library(data.table)
setDT(weekly, keep.rownames = TRUE)[]
weekly$rn <- as.Date(weekly$rn)
reservetionDatePlotCanceled <- weekly
ggplot(data = weekly,
      aes(x = rn))+
      geom_line(aes(y = V1, color = "Number of Reservations ReservationDate Canceled"))+
      ylab('Number of Reservations ReservationDate Canceled')+
      xlab('Date - Weekly')+
      theme(legend.position = "none")
Reservation Date Checkout
reservetion_dateCheckout <- yearMonthDay(y = checkoutTS$ArrivalDateYear,
                             m = checkoutTS$ArrivalDateMonth,
                             d = checkoutTS$ArrivalDateDayOfMonth)
reservetion_dateCheckout <- reservetion_dateCheckout - checkoutTS$LeadTime

reservetion_dateCheckout <- as.data.frame(reservetion_dateCheckout) 

a = reservetion_dateCheckout %>% group_by(reservetion_dateCheckout) %>% summarise(n = n())
data <- as.xts(a$n,order.by=as.Date(a$reservetion_dateCheckout))
weekly <- apply.weekly(data,sum)
weekly <- as.data.frame(weekly)

#index of a dataframe to columns
library(data.table)
setDT(weekly, keep.rownames = TRUE)[]
weekly$rn <- as.Date(weekly$rn)
reservetionDatePlotCheckout <- weekly
ggplot(data = weekly,
      aes(x = rn))+
      geom_line(aes(y = V1, color = "Number of Reservations ReservationDate Checkout"))+
      ylab('Number of Reservations ReservationDate Checkout')+
      xlab('Date - Weekly')+
      theme(legend.position = "none")
Reservation Date Checkout and Canceled
ggplot() +
  geom_line(data = reservetionDatePlotCheckout, aes(x = rn, y = V1, col = "Reservation Date Checkout")) + 
  geom_line(data = reservetionDatePlotCanceled, aes(x = rn, y = V1, col = "Reservetion Date Canceled"))+
  xlab('Date - Weeks')+
  ylab('Number of Reservations')+
  ggtitle('Number of Reservations')
Arrival Date for ADR
yearMonthDay <- function(y, m, d){
  date <- paste(as.character(y),paste(m, d, sep = "-"), sep = "-")
  date <- as.Date(date, "%Y-%m-%d")
}
arrival_date <- yearMonthDay(y = timeSeriesDataSet$ArrivalDateYear,
                             m = timeSeriesDataSet$ArrivalDateMonth,
                             d = timeSeriesDataSet$ArrivalDateDayOfMonth)

arrival_dateAdr<- as.data.frame(arrival_date) 
arrival_dateAdr$ADR <- raw_data$ADR
colnames(arrival_dateAdr) <- c("arrival_date", "ADR")

a = arrival_dateAdr %>% group_by(arrival_date) %>% summarize(totalMean=mean(ADR))

data <- as.xts(a$totalMean,order.by=as.Date(a$arrival_date))
weekly <- apply.weekly(data,mean)
weekly <- as.data.frame(weekly)

#index of a dataframe to columns
library(data.table)
setDT(weekly, keep.rownames = TRUE)[]
weekly$rn <- as.Date(weekly$rn)
arrivalDatePlotAdr <- weekly
ggplot(data = weekly,
      aes(x = rn))+
      geom_line(aes(y = V1, color = "ADR for ArrivalDate"))+
      ylab('ADR for that Arrival Date')+
      xlab('Date - Weekly')+
      theme(legend.position = "none")
Arrival Date, Arrival Date not canceled, Mean ADR Date
ggplot() +
  geom_line(data = arrivalDatePlot, aes(x = rn, y = V1, col = "Arrival Date")) + 
  geom_line(data = arrivalDatePlotAdr, aes(x = rn, y = V1, col = "Mean ADR Date"))+
  geom_line(data = arrivalDatePlotCheckout, aes(x = rn, y = V1, col = "Arrival Date not Canceled"))+
  xlab('Date - Weeks')+
  ylab('Number of Reservations')+
  ggtitle('Number of Reservations')
SEASONALITY
  #Transform to zoo data (forecast package)
  zReservationDateNumber=as.zoo(reservationDatePlot$V1)
  zArrivalDateNumber=as.zoo(arrivalDatePlot$V1)
  zAdrDateNumber = as.zoo(arrivalDatePlotAdr$V1)
  
  zArrivalDateCanceled = as.zoo(arrivalDatePlotCanceled$V1)
  zArrivalDateCheckout = as.zoo(arrivalDatePlotCheckout$V1)
  
  zReservationDateCanceled = as.zoo(reservetionDatePlotCanceled$V1) 
  zReservationDateCheckout = as.zoo(reservetionDatePlotCheckout$V1) 
 #Seasonal Plot
  ggfreqplot(as.ts(zReservationDateNumber),freq=4,nrow=1,facet.labeller=c("1T","2T","3T","4T"))+ggtitle("Number of Reservations - Reservation Date ")


 #Seasonal Plot
  ggfreqplot(as.ts(zArrivalDateNumber),freq=4,nrow=1,facet.labeller=c("1T","2T","3T","4T"))+ggtitle("Number of Reservations - Arrival Date ")

  
  #Seasonal Plot
  ggfreqplot(as.ts(zAdrDateNumber),freq=4,nrow=1,facet.labeller=c("1T","2T","3T","4T"))+ggtitle("Mean ADR  - Arrival Date ")

  
   #Seasonal Plot
  ggfreqplot(as.ts(zArrivalDateCanceled),freq=4,nrow=1,facet.labeller=c("1T","2T","3T","4T"))+ggtitle("Mean ADR  - Arrival Date ")


  #Seasonal Plot
  ggfreqplot(as.ts(zArrivalDateCheckout),freq=4,nrow=1,facet.labeller=c("1T","2T","3T","4T"))+ggtitle("Mean ADR  - Arrival Date ")

  
    #Seasonal Plot
  ggfreqplot(as.ts(zReservationDateCanceled),freq=4,nrow=1,facet.labeller=c("1T","2T","3T","4T"))+ggtitle("Mean ADR  - Arrival Date ")

  
   #Seasonal Plot
  ggfreqplot(as.ts(zReservationDateCheckout),freq=4,nrow=1,facet.labeller=c("1T","2T","3T","4T"))+ggtitle("Mean ADR  - Arrival Date ")

NA
STATIONARITY - ACF - PACF

Log transformation and differences

MODELING

ETS

TRAIN AND TEST DATA SET
#Select number of observation to compare forecast
cOmit=20

#Data Size
nObsArrival=length(zArrivalDateNumber)
oArrivalDateNumber <- window(zArrivalDateNumber,start=index(zArrivalDateNumber[1]),end=index(zArrivalDateNumber[nObsArrival-cOmit]))

#Data Size
nObsReservation=length(zReservationDateNumber)
oReservationDateNumber <- window(zReservationDateNumber,start=index(zReservationDateNumber[1]),end=index(zReservationDateNumber[nObsReservation-cOmit]))

#Data Size
nObsCanceled=length(zArrivalDateCanceled)
oArrivalDateCanceled <- window(zArrivalDateCanceled,start=index(zArrivalDateCanceled[1]),end=index(zArrivalDateCanceled[nObsCanceled-cOmit]))

#Data Size
nObsCheckout=length(zArrivalDateCheckout)
oArrivalDateCheckout <- window(zArrivalDateCheckout,start=index(zArrivalDateCheckout[1]),end=index(zArrivalDateCheckout[nObsCheckout-cOmit]))


#Data Size
nObsCheckout=length(zReservationDateCanceled)
oReservationDateCanceled <- window(zReservationDateCanceled,start=index(zReservationDateCanceled[1]),end=index(zReservationDateCanceled[nObsCheckout-cOmit]))

#Data Size
nObsCheckout=length(zReservationDateCheckout)
oReservationDateCheckout <- window(zReservationDateCheckout,start=index(zReservationDateCheckout[1]),end=index(zReservationDateCheckout[nObsCheckout-cOmit]))
TRAINING THE MODEL
etsfitArrival<-ets(oArrivalDateNumber, lambda = "auto", use.initial.values = TRUE, nmse = 30)
etsfitReservation<-ets(oReservationDateNumber, lambda = "auto", use.initial.values = TRUE, nmse = 30)

etsfitArrivalCanceled<-ets(oArrivalDateCanceled, lambda = "auto", use.initial.values = TRUE, nmse = 30)

etsfitArrivalCheckout<-ets(oArrivalDateCheckout, lambda = "auto", use.initial.values = TRUE, nmse = 30)

etsfitReservationCanceled<-ets(oReservationDateCanceled, lambda = "auto", use.initial.values = TRUE, nmse = 30)

etsfitReservationCheckout<-ets(oReservationDateCheckout, lambda = "auto", use.initial.values = TRUE, nmse = 30)
FORECAST
#forecast model
fArrival.ets=forecast(etsfitArrival, h = 20)
fReservation.ets=forecast(etsfitReservation, h = 20)
fCanceled.ets=forecast(etsfitArrivalCanceled, h = 20)
fCheckout.ets=forecast(etsfitArrivalCheckout, h = 20)

fCanceledReservation.ets=forecast(etsfitReservationCanceled, h = 20)
fCheckoutReservation.ets=forecast(etsfitReservationCheckout, h = 20)
plot(fArrival.ets)
lines(window(zArrivalDateNumber),type="o")
plot(fReservation.ets)
lines(window(zReservationDateNumber),type="o")
plot(fCanceled.ets)
lines(window(zArrivalDateCanceled),type="o")
plot(fCheckout.ets)
lines(window(zArrivalDateCheckout),type="o")
plot(fCanceledReservation.ets)
lines(window(zReservationDateCanceled),type="o")

plot(fCheckoutReservation.ets)
lines(window(zReservationDateCheckout),type="o")

ERROR MEASUREMENTS
Arrival Date
#Actual and Forecast
error_forecast_actual <- matrix(c(fArrival.ets$mean[1:cOmit],zArrivalDateNumber[(nObsArrival-cOmit+1):nObsArrival]),ncol=2)

#MAE
cat("MAE:",mean(abs(error_forecast_actual[,2]-error_forecast_actual[,1])))#1 forecast, 2 actual.
# [1] 345.3866
cat("\n\n")

#BIAS
bias = sum(error_forecast_actual[,1]-error_forecast_actual[,2]) * 1.0/length(error_forecast_actual[,2])
cat('Bias %:', bias)
Reservation Date
#Actual and Forecast
error_forecast_actual <- matrix(c(fReservation.ets$mean[1:cOmit],zReservationDateNumber[(nObsReservation-cOmit+1):nObsReservation]),ncol=2)

#MAE
cat("MAE:",mean(abs(error_forecast_actual[,2]-error_forecast_actual[,1])))#1 forecast, 2 actual.
# [1] 345.3866
cat("\n\n")

#BIAS
bias = sum(error_forecast_actual[,1]-error_forecast_actual[,2]) * 1.0/length(error_forecast_actual[,2])
cat('Bias %:', bias)
Arrival Date Canceled
#Actual and Forecast
error_forecast_actual <- matrix(c(fCanceled.ets$mean[1:cOmit],zArrivalDateCanceled[(nObsCanceled-cOmit+1):nObsCanceled]),ncol=2)

error_forecast_actualCanceled <- error_forecast_actual

#MAE
cat("MAE:",mean(abs(error_forecast_actual[,2]-error_forecast_actual[,1])))#1 forecast, 2 actual.
# [1] 345.3866
cat("\n\n")

#BIAS
bias = sum(error_forecast_actual[,1]-error_forecast_actual[,2]) * 1.0/length(error_forecast_actual[,2])
cat('Bias %:', bias)
Arrival Date Checkout
#Actual and Forecast
error_forecast_actual <- matrix(c(fCheckout.ets$mean[1:cOmit],zArrivalDateCheckout[(nObsCheckout-cOmit+1):nObsCheckout]),ncol=2)

error_forecast_actualCheckout <- error_forecast_actual

#MAE
cat("MAE:",mean(abs(error_forecast_actual[,2]-error_forecast_actual[,1])))#1 forecast, 2 actual.
# [1] 345.3866
cat("\n\n")

#BIAS
bias = sum(error_forecast_actual[,1]-error_forecast_actual[,2]) * 1.0/length(error_forecast_actual[,2])
cat('Bias %:', bias)
Checkout + Canceled Arrival Date
#Actual and Forecast
error_forecast_actual <- error_forecast_actualCanceled + error_forecast_actualCheckout

#MAE
cat("MAE:",mean(abs(error_forecast_actual[,2]-error_forecast_actual[,1])))#1 forecast, 2 actual.
# [1] 345.3866
cat("\n\n")

#BIAS
bias = sum(error_forecast_actual[,1]-error_forecast_actual[,2]) * 1.0/length(error_forecast_actual[,2])
cat('Bias %:', bias)
Reservation Date Canceled
#Actual and Forecast 
error_forecast_actual <-  matrix(c(fReservation.ets$mean[1:cOmit],zReservationDateCanceled[(nObsCanceled-cOmit+1):nObsCanceled]),ncol=2)

error_forecast_actualCanceledReservation <- error_forecast_actual

#MAE
cat("MAE:",mean(abs(error_forecast_actual[,2]-error_forecast_actual[,1])))#1 forecast, 2 actual.
MAE: 79.7721
# [1] 345.3866
cat("\n\n")
#BIAS
bias = sum(error_forecast_actual[,1]-error_forecast_actual[,2]) * 1.0/length(error_forecast_actual[,2])
cat('Bias %:', bias)
Bias %: 79.7721
Reservation Date Checkout
#Actual and Forecast
error_forecast_actual <-  matrix(c(fCheckoutReservation.ets$mean[1:cOmit],zReservationDateCheckout[(nObsCheckout-cOmit+1):nObsCheckout]),ncol=2)

error_forecast_actualCheckoutReservation <- error_forecast_actual

#MAE
cat("MAE:",mean(abs(error_forecast_actual[,2]-error_forecast_actual[,1])))#1 forecast, 2 actual.
MAE: 35.35768
# [1] 345.3866
cat("\n\n")
#BIAS
bias = sum(error_forecast_actual[,1]-error_forecast_actual[,2]) * 1.0/length(error_forecast_actual[,2])
cat('Bias %:', bias)
Bias %: 27.46537
Checkout + Canceled Reservation Date
#Actual and Forecast
error_forecast_actual <- error_forecast_actualCanceledReservation + error_forecast_actualCheckoutReservation

#MAE
cat("MAE:",mean(abs(error_forecast_actual[,2]-error_forecast_actual[,1])))#1 forecast, 2 actual.
MAE: 107.2375
# [1] 345.3866
cat("\n\n")
#BIAS
bias = sum(error_forecast_actual[,1]-error_forecast_actual[,2]) * 1.0/length(error_forecast_actual[,2])
cat('Bias %:', bias)
Bias %: 107.2375

ARIMA

TRAINING THE MODEL

# fit1Arrival=auto.arima(oArrivalDateNumber,lambda = 0)
fit1Arrival=auto.arima(oArrivalDateNumber, lambda = 0, start.p = 0, start.Q = 0, max.p = 10, max.q = 10, start.P = 0, start.q = 0,max.P = 10, max.Q = 10, max.d = 52, max.D = 52,seasonal = T)


summary(fit1Arrival)
#residual analysis
ggtsdisplay(fit1Arrival$residuals)
#box-Ljung Test, 3 porqué hemos estimado 3.
Box.test(fit1Arrival$residuals,lag=1, fitdf=3, type="Lj")
# Box.test(fit1Arrival$residuals,lag=8, fitdf=3, type="Lj")
# Box.test(fit1Arrival$residuals,lag=12, fitdf=3, type="Lj")
fit1Reservation=auto.arima(oReservationDateNumber, lambda = 0, start.p = 0, start.Q = 0, max.p = 10, max.q = 10, start.P = 0, start.q = 0,max.P = 10, max.Q = 10, max.d = 52, max.D = 52,seasonal = T)

summary(fit1Reservation)
#residual analysis
ggtsdisplay(fit1Reservation$residuals)
#box-Ljung Test, 3 porqué hemos estimado 3.
Box.test(fit1Reservation$residuals,lag=4, fitdf=3, type="Lj")
Box.test(fit1Reservation$residuals,lag=8, fitdf=3, type="Lj")
Box.test(fit1Reservation$residuals,lag=12, fitdf=3, type="Lj")
fit1Canceled=auto.arima(oArrivalDateCanceled, lambda = 0, start.p = 0, start.Q = 0, max.p = 10, max.q = 10, start.P = 0, start.q = 0,max.P = 10, max.Q = 10, max.d = 52, max.D = 52,seasonal = T)

summary(fit1Canceled)
#residual analysis
ggtsdisplay(fit1Canceled$residuals)
#box-Ljung Test, 3 porqué hemos estimado 3.
Box.test(fit1Canceled$residuals,lag=4, fitdf=3, type="Lj")
Box.test(fit1Canceled$residuals,lag=8, fitdf=3, type="Lj")
Box.test(fit1Canceled$residuals,lag=12, fitdf=3, type="Lj")
fit1Checkout=auto.arima(oArrivalDateCheckout, lambda = 0, start.p = 0, start.Q = 0, max.p = 10, max.q = 10, start.P = 0, start.q = 0,max.P = 10, max.Q = 10, max.d = 52, max.D = 52,seasonal = T)

summary(fit1Checkout)
#residual analysis
ggtsdisplay(fit1Checkout$residuals)
#box-Ljung Test, 3 porqué hemos estimado 3.
Box.test(fit1Checkout$residuals,lag=4, fitdf=3, type="Lj")
Box.test(fit1Checkout$residuals,lag=8, fitdf=3, type="Lj")
Box.test(fit1Checkout$residuals,lag=12, fitdf=3, type="Lj")
fit1CanceledReservation=auto.arima(oReservationDateCanceled, lambda = 0, start.p = 0, start.Q = 0, max.p = 10, max.q = 10, start.P = 0, start.q = 0,max.P = 10, max.Q = 10, max.d = 52, max.D = 52,seasonal = T)

summary(fit1CanceledReservation)
Series: oReservationDateCanceled 
ARIMA(1,1,1) 
Box Cox transformation: lambda= 0 

Coefficients:
         ar1      ma1
      0.4577  -0.7862
s.e.  0.1439   0.1009

sigma^2 estimated as 0.4038:  log likelihood=-122.73
AIC=251.45   AICc=251.64   BIC=260.01

Training set error measures:
                   ME    RMSE      MAE       MPE     MAPE
Training set 7.127027 46.2998 28.90803 -8.435588 43.42391
                  MASE       ACF1
Training set 0.8307652 -0.1353022
#residual analysis
ggtsdisplay(fit1CanceledReservation$residuals)

#box-Ljung Test, 3 porqué hemos estimado 3.
Box.test(fit1CanceledReservation$residuals,lag=4, fitdf=3, type="Lj")

    Box-Ljung test

data:  fit1CanceledReservation$residuals
X-squared = 6.168, df = 1, p-value = 0.01301
Box.test(fit1CanceledReservation$residuals,lag=8, fitdf=3, type="Lj")

    Box-Ljung test

data:  fit1CanceledReservation$residuals
X-squared = 12.839, df = 5, p-value = 0.02493
Box.test(fit1CanceledReservation$residuals,lag=12, fitdf=3, type="Lj")

    Box-Ljung test

data:  fit1CanceledReservation$residuals
X-squared = 18.765, df = 9, p-value = 0.02726
fit1CheckoutReservation=auto.arima(oReservationDateCheckout, lambda = 0, start.p = 0, start.Q = 0, max.p = 10, max.q = 10, start.P = 0, start.q = 0,max.P = 10, max.Q = 10, max.d = 52, max.D = 52,seasonal = T)

summary(fit1CheckoutReservation)
Series: oReservationDateCheckout 
ARIMA(0,1,1) 
Box Cox transformation: lambda= 0 

Coefficients:
          ma1
      -0.3107
s.e.   0.0879

sigma^2 estimated as 0.3172:  log likelihood=-117.82
AIC=239.63   AICc=239.72   BIC=245.52

Training set error measures:
                   ME     RMSE      MAE       MPE     MAPE
Training set 6.638264 92.11325 51.90594 -7.965977 34.87243
                  MASE       ACF1
Training set 0.9177611 -0.1112676
#residual analysis
ggtsdisplay(fit1CheckoutReservation$residuals)

#box-Ljung Test, 3 porqué hemos estimado 3.
Box.test(fit1CheckoutReservation$residuals,lag=4, fitdf=3, type="Lj")

    Box-Ljung test

data:  fit1CheckoutReservation$residuals
X-squared = 6.3696, df = 1, p-value = 0.01161
Box.test(fit1CheckoutReservation$residuals,lag=8, fitdf=3, type="Lj")

    Box-Ljung test

data:  fit1CheckoutReservation$residuals
X-squared = 14.4, df = 5, p-value = 0.01326
Box.test(fit1CheckoutReservation$residuals,lag=12, fitdf=3, type="Lj")

    Box-Ljung test

data:  fit1CheckoutReservation$residuals
X-squared = 20.795, df = 9, p-value = 0.01359

FORECAST

fArrival.arima=forecast(fit1Arrival, h = 20)

plot(fArrival.arima)
lines(window(zArrivalDateNumber),type="o")


res_ArimaMatrix <- matrix(c(fArrival.arima$mean[1:20],
                            as.double(tail(zArrivalDateNumber,n=20))),
                          ncol = 2)
res_ArimaMatrix
fReservation.arima=forecast(fit1Reservation, h = 20)

plot(fReservation.arima)
lines(window(zReservationDateNumber),type="o")


res_ArimaMatrix <- matrix(c(fReservation.arima$mean[1:20],
                            as.double(tail(zReservationDateNumber,n=20))),
                          ncol = 2)
res_ArimaMatrix
          [,1] [,2]
 [1,] 20.29468  183
 [2,] 17.17760  208
 [3,] 19.30864  197
 [4,] 17.78822  246
 [5,] 18.84139  191
 [6,] 18.09646  153
 [7,] 18.61574  166
 [8,] 18.25002  141
 [9,] 18.50574  133
[10,] 18.32603  151
[11,] 18.45188  152
[12,] 18.36353  173
[13,] 18.42545  170
[14,] 18.38200  120
[15,] 18.41246  105
[16,] 18.39109  106
[17,] 18.40608   78
[18,] 18.39557   77
[19,] 18.40294   54
[20,] 18.39777   16
fCanceled.arima=forecast(fit1Canceled, h = 20)

plot(fCanceled.arima)
lines(window(zArrivalDateCanceled),type="o")

res_ArimaMatrix <- matrix(c(fCanceled.arima$mean[1:20],
                            as.double(tail(zArrivalDateCanceled,n=20))),
                          ncol = 2)
res_ArimaMatrix
fCheckout.arima=forecast(fit1Checkout, h = 20)

plot(fCheckout.arima)
lines(window(zArrivalDateCheckout),type="o")


res_ArimaMatrix <- matrix(c(fCheckout.arima$mean[1:20],
                            as.double(tail(zArrivalDateCheckout,n=20))),
                          ncol = 2)
res_ArimaMatrix
          [,1] [,2]
 [1,] 249.5299  295
 [2,] 243.9061  292
 [3,] 238.9388  257
 [4,] 234.8387  278
 [5,] 231.7487  240
 [6,] 229.7488  337
 [7,] 228.8620  284
 [8,] 229.0601  251
 [9,] 230.2687  221
[10,] 232.3721  227
[11,] 235.2175  246
[12,] 238.6206  240
[13,] 242.3728  279
[14,] 246.2497  221
[15,] 250.0239  236
[16,] 253.4773  255
[17,] 256.4163  244
[18,] 258.6851  261
[19,] 260.1764  250
[20,] 260.8388  131
fCanceled.arimaReservation=forecast(fit1CanceledReservation, h = 20)

plot(fCanceled.arimaReservation)
lines(window(zReservationDateCanceled),type="o")


res_ArimaMatrix <- matrix(c(fCanceled.arimaReservation$mean[1:20],
                            as.double(tail(zReservationDateCanceled,n=20))),
                          ncol = 2)
res_ArimaMatrix
          [,1] [,2]
 [1,] 61.85637   45
 [2,] 66.38913   44
 [3,] 68.57294   42
 [4,] 69.59620   62
 [5,] 70.06957   83
 [6,] 70.28729   58
 [7,] 70.38716   37
 [8,] 70.43291   37
 [9,] 70.45385   43
[10,] 70.46344   37
[11,] 70.46783   45
[12,] 70.46984   44
[13,] 70.47076   54
[14,] 70.47118   49
[15,] 70.47137   24
[16,] 70.47146   21
[17,] 70.47150   22
[18,] 70.47152   18
[19,] 70.47153   16
[20,] 70.47153    5
fCheckout.arimaReservation=forecast(fit1CheckoutReservation, h = 20)

plot(fCheckout.arimaReservation)
lines(window(zReservationDateCheckout),type="o")


res_ArimaMatrix <- matrix(c(fCheckout.arimaReservation$mean[1:20],
                            as.double(tail(zReservationDateCheckout,n=20))),
                          ncol = 2)
res_ArimaMatrix
          [,1] [,2]
 [1,] 128.2248  139
 [2,] 128.2248  166
 [3,] 128.2248  135
 [4,] 128.2248  163
 [5,] 128.2248  133
 [6,] 128.2248  116
 [7,] 128.2248  129
 [8,] 128.2248   98
 [9,] 128.2248   96
[10,] 128.2248  106
[11,] 128.2248  108
[12,] 128.2248  119
[13,] 128.2248  121
[14,] 128.2248   96
[15,] 128.2248   84
[16,] 128.2248   84
[17,] 128.2248   60
[18,] 128.2248   61
[19,] 128.2248   49
[20,] 128.2248   16

ERROR MEASUREMENTS

Arrival
error_forecast<- as.data.frame(fArrival.arima$mean[1:20])#forecast
error_actual <- as.data.frame(tail(zArrivalDateNumber,n=20))#actual
dim(error_actual)[1]
[1] 20
error_actual_total <- error_actual

#MAE
cat("MAE:",mean(as.matrix(abs(error_forecast - error_actual))))#1 forecast, 2 actual.
MAE: 60.74243
cat("\n\n")
#BIAS
bias = sum(as.matrix(error_forecast - error_actual)) * 1.0/dim(error_actual)[1]
cat('Bias %:', bias)
Bias %: -45.46741
Reservation
error_forecast<- as.data.frame(fReservation.arima$mean[1:20])#forecast
error_actual <- as.data.frame(tail(zReservationDateNumber,n=20))#actual
dim(error_actual)[1]
[1] 20
error_actual_total <- error_actual

#MAE
cat("MAE:",mean(as.matrix(abs(error_forecast - error_actual))))#1 forecast, 2 actual.
MAE: 44.90987
cat("\n\n")
#BIAS
bias = sum(as.matrix(error_forecast - error_actual)) * 1.0/dim(error_actual)[1]
cat('Bias %:', bias)
Bias %: 22.71995
Canceled Arrival Date
error_forecastCanceled<- as.data.frame(fCanceled.arima$mean[1:20])#forecast
error_actualCanceled <- as.data.frame(tail(zArrivalDateCanceled,n=20))#actual
dim(error_actualCanceled)[1]
error_actual_total <- error_actualCanceled

#MAE
cat("MAE:",mean(as.matrix(abs(error_forecastCanceled - error_actualCanceled))))#1 forecast, 2 actual.

cat("\n\n")
#BIAS
bias = sum(as.matrix(error_forecastCanceled - error_actualCanceled)) * 1.0/dim(error_actualCanceled)[1]
cat('Bias %:', bias)
Checkout Arrival Date
error_forecastCheckout<- as.data.frame(fCheckout.arima$mean[1:20])#forecast
error_actualCheckout <- as.data.frame(tail(zArrivalDateCheckout,n=20))#actual
dim(error_actualCheckout)[1]
error_actual_total <- error_actualCheckout

#MAE
cat("MAE:",mean(as.matrix(abs(error_forecastCheckout - error_actualCheckout))))#1 forecast, 2 actual.

cat("\n\n")
#BIAS
bias = sum(as.matrix(error_forecastCheckout - error_actualCheckout)) * 1.0/dim(error_actualCheckout)[1]
cat('Bias %:', bias)
Checkout + Canceled Arrival Date
error_forecast<- error_forecastCanceled + error_forecastCheckout
error_actual <- error_actualCanceled + error_actualCheckout
dim(error_actual)[1]
[1] 20
error_actual_total <- error_actual

#MAE
cat("MAE:",mean(as.matrix(abs(error_forecast - error_actual))))#1 forecast, 2 actual.
MAE: 60.66275
cat("\n\n")
#BIAS
bias = sum(as.matrix(error_forecast - error_actual)) * 1.0/dim(error_actual)[1]
cat('Bias %:', bias)
Bias %: 54.63568
Canceled Reservation Date

error_forecastCanceled<- as.data.frame(fCanceled.arimaReservation$mean[1:20])#forecast
error_actualCanceled <- as.data.frame(tail(zReservationDateCanceled,n=20))#actual
dim(error_actualCanceled)[1]
[1] 20
error_actual_total <- error_actualCanceled

#MAE
cat("MAE:",mean(as.matrix(abs(error_forecastCanceled - error_actualCanceled))))#1 forecast, 2 actual.
MAE: 31.65391
cat("\n\n")
#BIAS
bias = sum(as.matrix(error_forecastCanceled - error_actualCanceled)) * 1.0/dim(error_actualCanceled)[1]
cat('Bias %:', bias)
Bias %: 30.36087
Checkout Reservation Date
error_forecastCheckout<-  as.data.frame(fCheckout.arimaReservation$mean[1:20])#forecast
error_actualCheckout <- as.data.frame(tail(zReservationDateCheckout,n=20))#actual
dim(error_actualCheckout)[1]
[1] 20
error_actual_total <- error_actualCheckout

#MAE
cat("MAE:",mean(as.matrix(abs(error_forecastCheckout - error_actualCheckout))))#1 forecast, 2 actual.
MAE: 33.83992
cat("\n\n")
#BIAS
bias = sum(as.matrix(error_forecastCheckout - error_actualCheckout)) * 1.0/dim(error_actualCheckout)[1]
cat('Bias %:', bias)
Bias %: 24.27481
Checkout + Canceled Reservation Date
error_forecast<- error_forecastCanceled + error_forecastCheckout
error_actual <- error_actualCanceled + error_actualCheckout
dim(error_actual)[1]
[1] 20
error_actual_total <- error_actual

#MAE
cat("MAE:",mean(as.matrix(abs(error_forecast - error_actual))))#1 forecast, 2 actual.
MAE: 60.66275
cat("\n\n")
#BIAS
bias = sum(as.matrix(error_forecast - error_actual)) * 1.0/dim(error_actual)[1]
cat('Bias %:', bias)
Bias %: 54.63568

FINAL PREDICTION

TRAINING THE MODEL
etsfitArrivalCanceled<-ets(zArrivalDateCanceled[-length(zArrivalDateCanceled)], lambda = "auto", use.initial.values = TRUE, nmse = 30)
etsfitArrivalCheckout<-ets(zArrivalDateCheckout[-length(zArrivalDateCheckout)], lambda = "auto", use.initial.values = TRUE, nmse = 30)


fit1Reservation=auto.arima(zReservationDateNumber, lambda = 0, start.p = 0, start.Q = 0, max.p = 10, max.q = 10, start.P = 0, start.q = 0,max.P = 10, max.Q = 10, max.d = 52, max.D = 52,seasonal = T)
FORECAST
#forecast model
fCanceled.ets=forecast(etsfitArrivalCanceled, h = 20)
fCheckout.ets=forecast(etsfitArrivalCheckout, h = 20)
plot(fCanceled.ets$mean + fCheckout.ets$mean, xlim = c(0,150), ylim = c(100, 600), main = 'Final Prediction', ylab = 'Number of Reservations for Arrival Date')
lines(window(zArrivalDateNumber[-length(zArrivalDateNumber)]),type="o")
fReservation.arima=forecast(fit1Reservation, h = 20)

plot(fReservation.arima)
lines(window(zReservationDateNumber),type="o")


res_ArimaMatrix <- matrix(c(fReservation.arima$mean[1:20],
                            as.double(tail(zReservationDateNumber,n=20))),
                          ncol = 2)
res_ArimaMatrix
          [,1] [,2]
 [1,] 20.29468  183
 [2,] 17.17760  208
 [3,] 19.30864  197
 [4,] 17.78822  246
 [5,] 18.84139  191
 [6,] 18.09646  153
 [7,] 18.61574  166
 [8,] 18.25002  141
 [9,] 18.50574  133
[10,] 18.32603  151
[11,] 18.45188  152
[12,] 18.36353  173
[13,] 18.42545  170
[14,] 18.38200  120
[15,] 18.41246  105
[16,] 18.39109  106
[17,] 18.40608   78
[18,] 18.39557   77
[19,] 18.40294   54
[20,] 18.39777   16
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQojIEFVVEhPUjogSk9SREkgVEFSUk9DSCBNRUrDk04KCkVsIGFyY2hpdm8gSDEgY29uc3RhIGRlIDQwLjA2MCBvYnNlcnZhY2lvbmVzIGVmZWN0dWFkYXMgc29icmUgMzEgdmFyaWFibGVzIGVuIHVuIGhvdGVsIGRlCnZhY2FjaW9uZXMgKHJlc29ydCksIHRhbCB5IGNvbW8gc2UgZGVzY3JpYmUgZW4gQW50b25pbywgQWxtZWlkYSB5IE51bmVzICgyMDE5KS4gU3UgdHJhYmFqbyBkZQpjYXJhIGEgbGEgZXZhbHVhY2nDs24gZGUgbGEgYXNpZ25hdHVyYSBjb25zaXN0aXLDoSBlbiBlbGFib3JhciB0cmVzIGRvY3VtZW50b3MgY29uIGxhcwpjYXJhY3RlcsOtc3RpY2FzIHNpZ3VpZW50ZXMuCgpFbCBwcmltZXJvLCBjb23Dum4gYSBlc3RhIGFzaWduYXR1cmEgeSBhIGxhIGRlbCBQcm9mLiBMw7NwZXogWmFmcmEsIGNvbnNpc3RpcsOhIGVuOgotIERlc2NyaWJpciBsYXMgb3BlcmFjaW9uZXMgZGUgbGltcGllemEgeSB0cmFuc2Zvcm1hY2nDs24gZGUgbG9zIGRhdG9zIGRlIGNhcmEgYSBsYSBjb25zb2xpZGFjacOzbiBkZSBsb3MgbWlzbW9zIHBhcmEgc3UgcG9zdGVyaW9yIGFwbGljYWNpw7NuIGEgY2FkYSB1bm8gZGUgbG9zIHRyYWJham9zIGVzcGVjw61maWNvcyBxdWUgZGViZXLDoSByZWFsaXphciBlbiBjYWRhIGFzaWduYXR1cmEuCgpFc3RlIGRvY3VtZW50bywg4oCcT3BlcmFjaW9uZXMgcHJlbGltaW5hcmVz4oCdLCB0ZW5kcsOhIGxhIGVzdHJ1Y3R1cmEgZGUgdW4gcGFwZXI6Ci0gdMOtdHVsbyAoZWwgc2XDsWFsYWRvKQotIGFic3RyYWN0Ci0gaW50cm9kdWNjacOzbiB5IG9iamV0aXZvcyBkZWwgdHJhYmFqbyBhIHJlYWxpemFyCi0gb3BlcmFjaW9uZXMgY29tdW5lcyBwYXJhIGxhcyBkb3MgYXNpZ25hdHVyYXM6IEVEQQotIG9wZXJhY2lvbmVzIGVzcGVjw61maWNhcyBjb3JyZXNwb25kaWVudGVzIGEgQWdydXBhY2nDs24KLSBvcGVyYWNpb25lcyBlc3BlY8OtZmljYXMgY29ycmVzcG9uZGllbnRlcyBhIFByZWRpY2Npw7NuCi0gY29uY2x1c2lvbmVzCi0gcmVmZXJlbmNpYXMgYmlibGlvZ3LDoWZpY2FzLgoKT1JJRU5UQUNJw5NOIGVuIGxhIGVzdHJ1Y3R1cmEgZGUgQW50b25pbywgQWxtZWlkYSB5IE51bmVzICgyMDE5KS4gTm8gZGViZQpyZXByb2R1Y2lyIG5pbmd1bmEgaW5mb3JtYWNpw7NuIHF1ZSBzZSBlbmN1ZW50cmUgZW4gZWwgY2l0YWRvIHBhcGVyLCBjb21vLCBwb3IgZWplbXBsbywgbGEKZGVzY3JpcGNpw7NuIGRlbCBhcmNoaXZvIHkgbGFzIHZhcmlhYmxlcy4gTGEgZXh0ZW5zacOzbiBtw6F4aW1hIGRlIGVzdGUgZG9jdW1lbnRvIHNlcsOhIGRlIDEwIHBwLgoKIyBMSUJSQVJJRVMKYGBge3J9CiMjIyMjIyMjIyMjIyMjCiNMSUJSQVJJRVMjIyMjCiMjIyMjIyMjIyMjIyMjCmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoc2tpbXIpIyBCZWF1dGlmdWwgU3VtbWFyaXplCmxpYnJhcnkoY2xlYW5kYXRhKSMgb3JkaW5hbCBlbmNvZGluZwpsaWJyYXJ5KG9uZWhvdCkjIG5vbWluYWwgZW5jb2RpbmcKCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkoZWFzeUdncGxvdDIpCmxpYnJhcnkoZm9yY2F0cykKCmxpYnJhcnkoUGVyZm9ybWFuY2VBbmFseXRpY3MpICMgQ29ycmVsYXRpb25zCmxpYnJhcnkoY29ycnBsb3QpCmxpYnJhcnkoZHBseXIpICMgc2VsZWN0CgpsaWJyYXJ5KGZhY3RvZXh0cmEpCmxpYnJhcnkoIk5iQ2x1c3QiKQpsaWJyYXJ5KGNsdXN0ZXIpCgpsaWJyYXJ5KHpvbykKbGlicmFyeShnZ2ZvcnRpZnkpCnJlcXVpcmUoZm9yZWNhc3QpCmBgYAoKYGBge3J9CnJhd19kYXRhPC1yZWFkX2NzdigiSDEuY3N2IikKYGBgCiMgVEFSR0VUIFZBUklBQkxFCi0gSXNDYW5jZWxlZAotIFJlc2VydmF0aW9uU3RhdHVzIGlzIGFsbW9zdCB0aGUgc2FtZSBhcyBJc0NhbmNlbGVkOgogIC0gTm8tU2hvdyBpcyBhIHR5cGUgb2YgQ2FuY2VsZWQgYm9va2luZy4KYGBge3J9CnVuaXF1ZShyYXdfZGF0YSRJc0NhbmNlbGVkW3Jhd19kYXRhJFJlc2VydmF0aW9uU3RhdHVzID09ICdDaGVjay1PdXQnXSkKdW5pcXVlKHJhd19kYXRhJElzQ2FuY2VsZWRbcmF3X2RhdGEkUmVzZXJ2YXRpb25TdGF0dXMgPT0gJ05vLVNob3cnXSkKdW5pcXVlKHJhd19kYXRhJElzQ2FuY2VsZWRbcmF3X2RhdGEkUmVzZXJ2YXRpb25TdGF0dXMgPT0gJ0NhbmNlbGVkJ10pCmBgYAoKIyBEQVRBIFdSQU5HTElORwoKCiMjIEVOQ09ESU5HIENBVEVHT1JJQ0FMIFZBUklBQkxFUwoKIyMjIE9SRElOQUwgRU5DT0RJTkcKLSBBcnJpdmFsRGF0ZU1vbnRoCgpgYGB7cn0KcmF3X2RhdGFfZW5jb2RlZCA9IHJhd19kYXRhCiMgT1JESU5BTCBFTkNPRElORwojQXJyaXZhbERhdGVNb250aApsZXZlbHMgPC0gYygnSmFudWFyeScsICdGZWJydWFyeScsICdNYXJjaCcsICdBcHJpbCcsICdNYXknLCAnSnVuZScsICdKdWx5JywgJ0F1Z3VzdCcsICdTZXB0ZW1iZXInLCAnT2N0b2JlcicsICdOb3ZlbWJlcicsICdEZWNlbWJlcicpCnJhd19kYXRhX2VuY29kZWQkQXJyaXZhbERhdGVNb250aCA9IGZhY3RvcihyYXdfZGF0YV9lbmNvZGVkJEFycml2YWxEYXRlTW9udGgsIG9yZGVyID0gVFJVRSAsIGxldmVscykKeCA8LSBhcy5kYXRhLmZyYW1lKHJhd19kYXRhX2VuY29kZWQkQXJyaXZhbERhdGVNb250aCkKcmF3X2RhdGFfZW5jb2RlZCRBcnJpdmFsRGF0ZU1vbnRoIDwtIGVuY29kZV9vcmRpbmFsKCB4LCBsZXZlbHMsIG5vbmU9JycsIG91dC5pbnQ9RkFMU0UsCiAgICAgICAgICAgICAgIGZ1bGxfcHJpbnQ9VFJVRSkKcmF3X2RhdGFfZW5jb2RlZCRBcnJpdmFsRGF0ZU1vbnRoIDwtIGFzLm51bWVyaWModW5saXN0KHJhd19kYXRhX2VuY29kZWQkQXJyaXZhbERhdGVNb250aCkpCgpwcmludChkaW0ocmF3X2RhdGFfZW5jb2RlZCkpCmBgYAoKIyMjICBOT01JTkFMIEVOQ09ESU5HIC0gT05FSE9UIEVOQ09ESU5HCi0gUmVzZXJ2ZWRSb29tVHlwZQotIEFzc2lnbmVkUm9vbVR5cGUKLSBNZWFsCi0gQ291bnRyeQotIE1hcmtldFNlZ21lbnQKLSBEaXN0cmlidXRpb25DaGFubmVsCi0gRGVwb3NpdFR5cGUKLSBDdXN0b21lclR5cGUKCgpgYGB7cn0KIyBOT01JTkFMIEVOQ09ESU5HIC0gT05FSE9UIEVOQ09ESU5HCgojIGh0dHBzOi8vZ2l0aHViLmNvbS9aZWxhem55Ny9vbmVob3QKIyBSZXNlcnZlZFJvb21UeXBlClJlc2VydmVkUm9vbVR5cGUgPC0gYXMuZGF0YS5mcmFtZShyYXdfZGF0YV9lbmNvZGVkJFJlc2VydmVkUm9vbVR5cGUpCmVuY29kZXIgPC0gb25laG90KFJlc2VydmVkUm9vbVR5cGUsIG1heF9sZXZlbHMgPSAxNSwgYWRkX05BX2ZhY3RvcnMgPSBGQUxTRSkKUmVzZXJ2ZWRSb29tVHlwZV9vbmVob3Q8LSBwcmVkaWN0KGVuY29kZXIsIFJlc2VydmVkUm9vbVR5cGUsIHN0cmluZ3NBc0ZhY3RvcnM9VFJVRSkKcmF3X2RhdGFfZW5jb2RlZCA8LSBjYmluZChyYXdfZGF0YV9lbmNvZGVkLCBSZXNlcnZlZFJvb21UeXBlX29uZWhvdCkKcHJpbnQoZGltKHJhd19kYXRhX2VuY29kZWQpKQoKIyBBc3NpZ25lZFJvb21UeXBlCkFzc2lnbmVkUm9vbVR5cGUgPC0gYXMuZGF0YS5mcmFtZShyYXdfZGF0YV9lbmNvZGVkJEFzc2lnbmVkUm9vbVR5cGUpCmVuY29kZXIgPC0gb25laG90KEFzc2lnbmVkUm9vbVR5cGUsIG1heF9sZXZlbHMgPSAxNSwgYWRkX05BX2ZhY3RvcnMgPSBGQUxTRSkKQXNzaWduZWRSb29tVHlwZV9vbmVob3Q8LSBwcmVkaWN0KGVuY29kZXIsIEFzc2lnbmVkUm9vbVR5cGUsIHN0cmluZ3NBc0ZhY3RvcnM9VFJVRSkKcmF3X2RhdGFfZW5jb2RlZCA8LSBjYmluZChyYXdfZGF0YV9lbmNvZGVkLCBBc3NpZ25lZFJvb21UeXBlX29uZWhvdCkKcHJpbnQoZGltKHJhd19kYXRhX2VuY29kZWQpKQoKIyBNZWFsCk1lYWwgPC0gYXMuZGF0YS5mcmFtZShyYXdfZGF0YV9lbmNvZGVkJE1lYWwpCmVuY29kZXIgPC0gb25laG90KE1lYWwsIG1heF9sZXZlbHMgPSAxNSwgYWRkX05BX2ZhY3RvcnMgPSBGQUxTRSkKTWVhbF9vbmVob3Q8LSBwcmVkaWN0KGVuY29kZXIsIE1lYWwsIHN0cmluZ3NBc0ZhY3RvcnM9VFJVRSkKcmF3X2RhdGFfZW5jb2RlZCA8LSBjYmluZChyYXdfZGF0YV9lbmNvZGVkLCBNZWFsX29uZWhvdCkKcHJpbnQoZGltKHJhd19kYXRhX2VuY29kZWQpKQoKIyBDb3VudHJ5CkNvdW50cnkgPC0gYXMuZGF0YS5mcmFtZShyYXdfZGF0YV9lbmNvZGVkJENvdW50cnkpCmVuY29kZXIgPC0gb25laG90KENvdW50cnksIG1heF9sZXZlbHMgPSAxMzAsIGFkZF9OQV9mYWN0b3JzID0gRkFMU0UpCkNvdW50cnlfb25laG90PC0gcHJlZGljdChlbmNvZGVyLCBDb3VudHJ5LCBzdHJpbmdzQXNGYWN0b3JzPVRSVUUpCnJhd19kYXRhX2VuY29kZWQgPC0gY2JpbmQocmF3X2RhdGFfZW5jb2RlZCwgQ291bnRyeV9vbmVob3QpCnByaW50KGRpbShyYXdfZGF0YV9lbmNvZGVkKSkKCiMgTWFya2V0U2VnbWVudApNYXJrZXRTZWdtZW50IDwtIGFzLmRhdGEuZnJhbWUocmF3X2RhdGFfZW5jb2RlZCRNYXJrZXRTZWdtZW50KQplbmNvZGVyIDwtIG9uZWhvdChNYXJrZXRTZWdtZW50LCBtYXhfbGV2ZWxzID0gMTUsIGFkZF9OQV9mYWN0b3JzID0gRkFMU0UpCk1hcmtldFNlZ21lbnRfb25laG90PC0gcHJlZGljdChlbmNvZGVyLCBNYXJrZXRTZWdtZW50LCBzdHJpbmdzQXNGYWN0b3JzPVRSVUUpCnJhd19kYXRhX2VuY29kZWQgPC0gY2JpbmQocmF3X2RhdGFfZW5jb2RlZCwgTWFya2V0U2VnbWVudF9vbmVob3QpCgojIERpc3RyaWJ1dGlvbkNoYW5uZWwKRGlzdHJpYnV0aW9uQ2hhbm5lbCA8LSBhcy5kYXRhLmZyYW1lKHJhd19kYXRhX2VuY29kZWQkRGlzdHJpYnV0aW9uQ2hhbm5lbCkKZW5jb2RlciA8LSBvbmVob3QoRGlzdHJpYnV0aW9uQ2hhbm5lbCwgbWF4X2xldmVscyA9IDE1LCBhZGRfTkFfZmFjdG9ycyA9IEZBTFNFKQpEaXN0cmlidXRpb25DaGFubmVsX29uZWhvdDwtIHByZWRpY3QoZW5jb2RlciwgRGlzdHJpYnV0aW9uQ2hhbm5lbCwgc3RyaW5nc0FzRmFjdG9ycz1UUlVFKQpyYXdfZGF0YV9lbmNvZGVkIDwtIGNiaW5kKHJhd19kYXRhX2VuY29kZWQsIERpc3RyaWJ1dGlvbkNoYW5uZWxfb25laG90KQoKCiMgRGVwb3NpdFR5cGUKRGVwb3NpdFR5cGUgPC0gYXMuZGF0YS5mcmFtZShyYXdfZGF0YV9lbmNvZGVkJERlcG9zaXRUeXBlKQplbmNvZGVyIDwtIG9uZWhvdChEZXBvc2l0VHlwZSwgbWF4X2xldmVscyA9IDE1LCBhZGRfTkFfZmFjdG9ycyA9IEZBTFNFKQpEZXBvc2l0VHlwZV9vbmVob3Q8LSBwcmVkaWN0KGVuY29kZXIsIERlcG9zaXRUeXBlLCBzdHJpbmdzQXNGYWN0b3JzPVRSVUUpCnJhd19kYXRhX2VuY29kZWQgPC0gY2JpbmQocmF3X2RhdGFfZW5jb2RlZCwgRGVwb3NpdFR5cGVfb25laG90KQoKCiMgQ3VzdG9tZXJUeXBlCkN1c3RvbWVyVHlwZSA8LSBhcy5kYXRhLmZyYW1lKHJhd19kYXRhX2VuY29kZWQkQ3VzdG9tZXJUeXBlKQplbmNvZGVyIDwtIG9uZWhvdChDdXN0b21lclR5cGUsIG1heF9sZXZlbHMgPSAxNSwgYWRkX05BX2ZhY3RvcnMgPSBGQUxTRSkKQ3VzdG9tZXJUeXBlX29uZWhvdDwtIHByZWRpY3QoZW5jb2RlciwgQ3VzdG9tZXJUeXBlLCBzdHJpbmdzQXNGYWN0b3JzPVRSVUUpCnJhd19kYXRhX2VuY29kZWQgPC0gY2JpbmQocmF3X2RhdGFfZW5jb2RlZCwgQ3VzdG9tZXJUeXBlX29uZWhvdCkKCmBgYAoKIyMjIE5VTEwgdG8gTlVNRVJJQyAKLSBBZ2VudAotIENvbXBhbnkKCmBgYHtyfQojIEFnZW50CnJhd19kYXRhX2VuY29kZWQkQWdlbnQgPC0gaWZlbHNlKHJhd19kYXRhX2VuY29kZWQkQWdlbnQgPT0gIk5VTEwiLCAwLHJhd19kYXRhX2VuY29kZWQkQWdlbnQpCnJhd19kYXRhX2VuY29kZWQkQWdlbnQgPC0gYXMubnVtZXJpYyhyYXdfZGF0YV9lbmNvZGVkJEFnZW50KQojIENvbXBhbnkKcmF3X2RhdGFfZW5jb2RlZCRDb21wYW55IDwtIGlmZWxzZShyYXdfZGF0YV9lbmNvZGVkJENvbXBhbnkgPT0gIk5VTEwiLCAwLHJhd19kYXRhX2VuY29kZWQkQ29tcGFueSkKcmF3X2RhdGFfZW5jb2RlZCRDb21wYW55IDwtIGFzLm51bWVyaWMocmF3X2RhdGFfZW5jb2RlZCRDb21wYW55KQpgYGAKCiMjIyMgQUdFTlQgWUVTCmBgYHtyfQpyYXdfZGF0YV9lbmNvZGVkJEFnZW50WWVzIDwtIGlmZWxzZShyYXdfZGF0YV9lbmNvZGVkJEFnZW50ID09IDAsIDAsIDEpCmBgYAoKIyMjIyBDT01QQU5ZIFlFUwpgYGB7cn0KcmF3X2RhdGFfZW5jb2RlZCRDb21wYW55WWVzIDwtIGlmZWxzZShyYXdfZGF0YV9lbmNvZGVkJENvbXBhbnkgPT0gMCwgMCwgMSkKYGBgCgojIyBQUkUtRU5DT0RFRCBBTkQgQ09NTU9OIFNFTlNFIENPTFVNTlMgREVMRVRJT04KYGBge3J9CnRpbWVTZXJpZXNEYXRhU2V0ID0gcmF3X2RhdGFfZW5jb2RlZAojIEVSQVNFIEVOQ09ERUQgVkFSSUFCTEVTCmVyYXNlX2NvbHVtbnMgPC0gYygxMiwgMTMsIDE0LCAxNSwgMTksIDIwLCAyMiwgMjYpCmVyYXNlX2NvbHVtbnMgIyBudW1iZXIgb2YgdGhlIHBvc2l0aW9uIG9mIHRoZSBjb2x1bW5zIHRvIGVyYXNlCnJhd19kYXRhX2VuY29kZWQgPC0gcmF3X2RhdGFfZW5jb2RlZFsgLWVyYXNlX2NvbHVtbnMgXQoKIyBDT01NT04gU0VOU0UgREVMRVRJT046IEJhc2VkIG9uIGhvdyB3ZSBkZWZpbmUgdGhlIFRhcmdldCBWYXJpYWJsZQojIFJlc2VydmF0aW9uU3RhdHVzIGFuZCBEYXRlCnJhd19kYXRhX2VuY29kZWQgPC0gcmF3X2RhdGFfZW5jb2RlZFsgLWMoMjIsMjMpIF0KYGBgCgoKIyMgU0FNUExJTkcKYGBge3J9CnNldC5zZWVkKDEyMykKI0VuY29kZWQgZGF0YSBzYW1wbGUKcmF3X2RhdGFfZW5jb2RlZF9zYW1wbGUgPSBzYW1wbGVfbihyYXdfZGF0YV9lbmNvZGVkLCBzaXplID0gMTAwMCkKYGBgCgoKIyMjIFRBUkdFVCBWQVJJQUJMRQpgYGB7cn0KI2lzQ2FuY2VsZWQKaXNDYW5jZWxlZF9zYW1wbGUgPSByYXdfZGF0YV9lbmNvZGVkX3NhbXBsZVssMV0KYGBgCmBgYHtyfQp0YXJnZXQgPC0gYXMuZGF0YS5mcmFtZShyYXdfZGF0YV9lbmNvZGVkX3NhbXBsZSAlPiVncm91cF9ieShJc0NhbmNlbGVkKSU+JXN1bW1hcmlzZShjb3VudHMgPSBuKCkpKSU+JW11dGF0ZShwZXJjID0gY291bnRzL25yb3cocmF3X2RhdGFfZW5jb2RlZF9zYW1wbGUpKQoKZ2dwbG90KHRhcmdldCwgYWVzKHggPSBJc0NhbmNlbGVkLCB5ID0gcGVyYykpICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZChwZXJjLDIpKSkKYGBgCiMjIyBQUkVESUNUSVZFIFZBUklBQkxFUwpgYGB7cn0KI1ByZWRpY3RpdmUgVmFyaWFibGVzOiBhbGwgb2YgdGhlbSBleGNlcHQ6CiAgIyAtIElzQ2FuY2VsZWQgKFRhcmdldCBWYXJpYWJsZSkgYW5kIEFycml2YWxEYXRlWWVhci4KIyBXZSBlcmFzZSBBcnJpdmFsRGF0ZVllYXIgYmVjYXVzZSB3ZSB3b24ndCBvdXIgbW9kZWxzIHRvIGJlIHRpbWUgcmVwbGljYWJsZS4KcHJlZGljdGl2ZVZhcmlhYmxlc19zYW1wbGUgPSByYXdfZGF0YV9lbmNvZGVkX3NhbXBsZVssYygtMSwtMyldCmBgYAoKIyMgUFJFRElDVElPTiBEQVRBU0VUCmBgYHtyfQpyYXdfZGF0YV9wcmVkaWN0aW9uIDwtIHJhd19kYXRhX2VuY29kZWRbLTNdCgpyYXdfZGF0YV9wcmVkaWN0aW9uX3NhbXBsZSA8LSByYXdfZGF0YV9lbmNvZGVkX3NhbXBsZVstM10KcmF3X2RhdGFfcHJlZGljdGlvbl9zYW1wbGUgPSByYXdfZGF0YV9wcmVkaWN0aW9uX3NhbXBsZVshc2FwcGx5KHJhd19kYXRhX3ByZWRpY3Rpb25fc2FtcGxlLCBmdW5jdGlvbih4KSBzdW0oeCk9PSAwKV0KYGBgCgoKCgojIEVEQQojIyBUYXJnZXQgVmFyaWFibGUgJXgKYGBge3J9CnRhcmdldCA8LSBhcy5kYXRhLmZyYW1lKHJhd19kYXRhICU+JWdyb3VwX2J5KElzQ2FuY2VsZWQpJT4lc3VtbWFyaXNlKGNvdW50cyA9IG4oKSkpJT4lbXV0YXRlKHBlcmMgPSBjb3VudHMvbnJvdyhyYXdfZGF0YSkpCgpnZ3Bsb3QodGFyZ2V0LCBhZXMoeCA9IElzQ2FuY2VsZWQsIHkgPSBwZXJjKSkgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikrCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKHBlcmMsMikpKQpgYGAKCgoKIyMgUmVsYXRpb25zaGlwcwogIEdlbmVyYWwgSGlzdG9ncmFtCiAgSGlzdG9ncmFtIGJhc2VkIG9uIHR5cGUgMC0xCiAgQm94cGxvdAoKCmBgYHtyfQojUGxvdCBmdW5jdGlvbgplZGFfcGxvdCA8LSBmdW5jdGlvbihkZl9mdW5jdGlvbiwgY2hhcmFjdGVyLCBjb2x1bW4pewogIGRmX2Z1bmN0aW9uIDwtIGRmX2Z1bmN0aW9uWyxjKDEsY29sdW1uKV0KICBjb2xuYW1lcyhkZl9mdW5jdGlvbikgPC0gYygiSXNDYW5jZWxlZCIsICJWYXJpYWJsZSIgKQogICMjIyMjIyMjIyMjIyMKICAjIEhJU1RPR1JBTVMjCiAgIyMjIyMjIyMjIyMjIwogIGJ4cCA8LSBnZ3Bsb3QoZGZfZnVuY3Rpb24pICsKICAgIGdlb21fZGVuc2l0eShhZXMoeCA9IFZhcmlhYmxlLCBmaWxsID0gSXNDYW5jZWxlZCksIGFscGhhID0gMC4yKSsKICAgIHhsYWIoY2hhcmFjdGVyKQoKICBkcCA8LSBnZ3Bsb3QyLmhpc3RvZ3JhbShkYXRhPWRmX2Z1bmN0aW9uLCB4TmFtZT0gJ1ZhcmlhYmxlJywgYWRkTWVhbkxpbmU9VFJVRSwgbWVhbkxpbmVDb2xvcj0iZ3JlZW4iLAogICAgICAgICAgICAgICAgICAgIG1lYW5MaW5lVHlwZT0iZGFzaGVkIiwgbWVhbkxpbmVTaXplPTEsCiAgICAgICAgICAgICAgICAgICAgYWRkRGVuc2l0eUN1cnZlPVRSVUUsIGRlbnNpdHlGaWxsPSdibHVlJywgZmlsbCA9ICdibHVlJykrIHhsYWIoY2hhcmFjdGVyKQoKICAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiAgIyBSRUxBVElPTlNISVBTIEJFVFdFRU4gVkFSSUFCTEVTIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwogICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIGJwIDwtIGRmX2Z1bmN0aW9uICU+JQogICBtdXRhdGUoY2xhc3MgPSBmY3RfcmVvcmRlcihJc0NhbmNlbGVkLFZhcmlhYmxlLCAuZnVuPSdsZW5ndGgnICkpICU+JQogICBnZ3Bsb3QoIGFlcyh4PUlzQ2FuY2VsZWQsIHk9IFZhcmlhYmxlLCBmaWxsPWNsYXNzKSkgKwogICAgIGdlb21fYm94cGxvdCgpICsKICAgICB4bGFiKCJJc0NhbmNlbGVkIikgKwogICAgIHlsYWIoY2hhcmFjdGVyKSsKICAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgICAgeGxhYigiIikgKwogICAgIHhsYWIoIiIpCgogIGdnYXJyYW5nZShieHAsIGRwLCBicCAsCiAgICAgICAgICBsYWJlbHMgPSBjKCJBIiwgIkIiLCAiQyIpLAogICAgICAgICAgbmNvbCA9IDIsIG5yb3cgPSAyKQoKfQoKcmF3X2RhdGEkSXNDYW5jZWxlZCA8LSBmYWN0b3IocmF3X2RhdGEkSXNDYW5jZWxlZCkKYGBgCgpgYGB7cn0KI1Bsb3QgZnVuY3Rpb24KZWRhX3Bsb3RfY2F0ZWdvcmljYWwgPC0gZnVuY3Rpb24oZGZfZnVuY3Rpb24sIGNoYXJhY3RlciwgY29sdW1uKXsKICBkZl9mdW5jdGlvbiA8LSBkZl9mdW5jdGlvblssYygxLGNvbHVtbildCiAgY29sbmFtZXMoZGZfZnVuY3Rpb24pIDwtIGMoIklzQ2FuY2VsZWQiLCAiVmFyaWFibGUiICkKICAjIyMjIyMjIyMjIyMjCiAgIyBISVNUT0dSQU1TIwogICMjIyMjIyMjIyMjIyMKICBieHAgPC0gZ2dwbG90KGRmX2Z1bmN0aW9uKSArCiAgICBnZW9tX2RlbnNpdHkoYWVzKHggPSBWYXJpYWJsZSwgZmlsbCA9IElzQ2FuY2VsZWQpLCBhbHBoYSA9IDAuMikrCiAgICB4bGFiKGNoYXJhY3RlcikKCiAgZHAgPC0gZ2dwbG90Mi5oaXN0b2dyYW0oZGF0YT1kZl9mdW5jdGlvbiwgeE5hbWU9ICdWYXJpYWJsZScsIGFkZE1lYW5MaW5lPVRSVUUsIG1lYW5MaW5lQ29sb3I9ImdyZWVuIiwKICAgICAgICAgICAgICAgICAgICBtZWFuTGluZVR5cGU9ImRhc2hlZCIsIG1lYW5MaW5lU2l6ZT0xLAogICAgICAgICAgICAgICAgICAgIGFkZERlbnNpdHlDdXJ2ZT1UUlVFLCBkZW5zaXR5RmlsbD0nYmx1ZScsIGZpbGwgPSAnYmx1ZScpKyB4bGFiKGNoYXJhY3RlcikKCgoKZ2dhcnJhbmdlKGJ4cCwgZHAsCiAgICAgICAgICBsYWJlbHMgPSBjKCJBIiwgIkIiKSwKICAgICAgICAgIG5jb2wgPSAyKQp9CmBgYAoKYGBge3J9CiNQbG90IGZ1bmN0aW9uCmVkYV9wbG90X2NhdGVnb3JpY2FsIDwtIGZ1bmN0aW9uKGRmX2Z1bmN0aW9uLCBjaGFyYWN0ZXIsIGNvbHVtbil7CiAgZGZfZnVuY3Rpb24gPC0gZGZfZnVuY3Rpb25bLGMoMSxjb2x1bW4pXQogIGNvbG5hbWVzKGRmX2Z1bmN0aW9uKSA8LSBjKCJJc0NhbmNlbGVkIiwgIlZhcmlhYmxlIiApCiAgIyMjIyMjIyMjIyMjIwogICMgSElTVE9HUkFNUyMKICAjIyMjIyMjIyMjIyMjCiAgYnhwIDwtIGdncGxvdChkZl9mdW5jdGlvbikgKwogICAgZ2VvbV9kZW5zaXR5KGFlcyh4ID0gVmFyaWFibGUsIGZpbGwgPSBJc0NhbmNlbGVkKSwgYWxwaGEgPSAwLjIpKwogICAgeGxhYihjaGFyYWN0ZXIpCiAgYnhwCn0KYGBgCgpgYGB7cn0KZWRhX3Bsb3QocmF3X2RhdGEsICJMZWFkVGltZSIsIGNvbHVtbiA9IDIpCmBgYApgYGB7cn0KZWRhX3Bsb3QocmF3X2RhdGEsICJBcnJpdmFsRGF0ZVllYXIiLCBjb2x1bW4gPSAzKQpgYGAKCgpgYGB7cn0KZWRhX3Bsb3RfY2F0ZWdvcmljYWwocmF3X2RhdGEsICJBcnJpdmFsRGF0ZU1vbnRoIiwgY29sdW1uID0gNCkKYGBgCgpgYGB7cn0KZWRhX3Bsb3QocmF3X2RhdGEsICJBcnJpdmFsRGF0ZVdlZWtOdW1iZXIiLCBjb2x1bW4gPSA1KQpgYGAKYGBge3J9CmVkYV9wbG90KHJhd19kYXRhLCAiQXJyaXZhbERhdGVEYXlPZk1vbnRoIiwgY29sdW1uID0gNikKYGBgCgpgYGB7cn0KZWRhX3Bsb3QocmF3X2RhdGEsICJTdGF5c0luV2Vla2VuZE5pZ2h0cyIsIGNvbHVtbiA9IDcpCmBgYAoKYGBge3J9CmVkYV9wbG90KHJhd19kYXRhLCAiU3RheXNJbldlZWtOaWdodHMiLCBjb2x1bW4gPSA4KQpgYGAKCmBgYHtyfQplZGFfcGxvdChyYXdfZGF0YSwgIkFkdWx0cyIsIGNvbHVtbiA9IDkpCmBgYApgYGB7cn0KZWRhX3Bsb3QocmF3X2RhdGEsICJDaGlsZHJlbiIsIGNvbHVtbiA9IDEwKQpgYGAKYGBge3J9CmVkYV9wbG90KHJhd19kYXRhLCAiQmFiaWVzIiwgY29sdW1uID0gMTEpCmBgYApgYGB7cn0KZWRhX3Bsb3RfY2F0ZWdvcmljYWwocmF3X2RhdGEsICJNZWFsIiwgY29sdW1uID0gMTIpCmBgYApgYGB7cn0KZWRhX3Bsb3RfY2F0ZWdvcmljYWwocmF3X2RhdGEsICJDb3VudHJ5IiwgY29sdW1uID0gMTMpCmBgYApgYGB7cn0KZWRhX3Bsb3RfY2F0ZWdvcmljYWwocmF3X2RhdGEsICJNYXJrZXRTZWdtZW50IiwgY29sdW1uID0gMTQpCmBgYApgYGB7cn0KZWRhX3Bsb3RfY2F0ZWdvcmljYWwocmF3X2RhdGEsICJEaXN0cmlidXRpb25DaGFubmVsIiwgY29sdW1uID0gMTUpCmBgYApgYGB7cn0KZWRhX3Bsb3QocmF3X2RhdGEsICJJc1JlcGVhdGVkR3Vlc3QiLCBjb2x1bW4gPSAxNikKYGBgCmBgYHtyfQplZGFfcGxvdChyYXdfZGF0YSwgIlByZXZpb3VzQ2FuY2VsbGF0aW9ucyIsIGNvbHVtbiA9IDE3KQpgYGAKYGBge3J9CmVkYV9wbG90KHJhd19kYXRhLCAiUHJldmlvdXNCb29raW5nc05vdENhbmNlbGVkIiwgY29sdW1uID0gMTgpCmBgYApgYGB7cn0KZWRhX3Bsb3RfY2F0ZWdvcmljYWwocmF3X2RhdGEsICJSZXNlcnZlZFJvb21UeXBlIiwgY29sdW1uID0gMTkpCmBgYApgYGB7cn0KZWRhX3Bsb3RfY2F0ZWdvcmljYWwocmF3X2RhdGEsICJBc3NpZ25lZFJvb21UeXBlIiwgY29sdW1uID0gMjApCmBgYApgYGB7cn0KZWRhX3Bsb3QocmF3X2RhdGEsICJCb29raW5nQ2hhbmdlcyIsIGNvbHVtbiA9IDIxKQpgYGAKYGBge3J9CmVkYV9wbG90X2NhdGVnb3JpY2FsKHJhd19kYXRhLCAiRGVwb3NpdFR5cGUiLCBjb2x1bW4gPSAyMikKYGBgCmBgYHtyfQoKZWRhX3Bsb3RfY2F0ZWdvcmljYWwocmF3X2RhdGEsICJBZ2VudCIsIGNvbHVtbiA9IDIzKQpgYGAKYGBge3J9CmVkYV9wbG90X2NhdGVnb3JpY2FsKHJhd19kYXRhLCAiQ29tcGFueSIsIGNvbHVtbiA9IDI0KQpgYGAKYGBge3J9CmVkYV9wbG90X2NhdGVnb3JpY2FsKHJhd19kYXRhLCAiRGF5c0luV2FpdGluZ0xpc3QiLCBjb2x1bW4gPSAyNSkKYGBgCgpgYGB7cn0KZWRhX3Bsb3RfY2F0ZWdvcmljYWwocmF3X2RhdGEsICJDdXN0b21lclR5cGUiLCBjb2x1bW4gPSAyNikKYGBgCgpgYGB7cn0KZWRhX3Bsb3QocmF3X2RhdGEsICJBRFIiLCBjb2x1bW4gPSAyNykKYGBgCmBgYHtyfQplZGFfcGxvdChyYXdfZGF0YSwgIlJlcXVpcmVkQ2FyUGFya2luZ1NwYWNlcyIsIGNvbHVtbiA9IDI4KQpgYGAKYGBge3J9CmVkYV9wbG90KHJhd19kYXRhLCAiVG90YWxPZlNwZWNpYWxSZXF1ZXN0cyIsIGNvbHVtbiA9IDI5KQpgYGAKYGBge3J9CmVkYV9wbG90X2NhdGVnb3JpY2FsKHJhd19kYXRhLCAiUmVzZXJ2YXRpb25TdGF0dXMiLCBjb2x1bW4gPSAzMCkKYGBgCgpgYGB7cn0KcmF3X2RhdGFfZW5jb2RlZCRJc0NhbmNlbGVkIDwtIGZhY3RvcihyYXdfZGF0YV9lbmNvZGVkJElzQ2FuY2VsZWQpCmVkYV9wbG90X2NhdGVnb3JpY2FsKHJhd19kYXRhX2VuY29kZWQsICJBZ2VudFllcyIsIGNvbHVtbiA9IDE5MSkKYGBgCgpgYGB7cn0KZWRhX3Bsb3RfY2F0ZWdvcmljYWwocmF3X2RhdGFfZW5jb2RlZCwgIkNvbXBhbnlZZXMiLCBjb2x1bW4gPSAxOTIpCmBgYAojIyBNYXRyaXggQ29ycmVsYXRpb24KLSBOdW1lcmljIGFuZCBPcmRpbmFsIEVuY29kZWQgVmFyaWFibGVzIChmcm9tIGNvbHVtbiAyIHRvIDIxKQoKCmBgYHtyfQoKY29ycnBsb3QoY29yKHJhd19kYXRhX2VuY29kZWRbLDI6MjFdLAogICAgICAgICAgICAgdXNlID0gImNvbXBsZXRlLm9icyIpLAogICAgICAgICBtZXRob2QgPSAiY2lyY2xlIiwKICAgICAgICAgdHlwZSA9ICd1cHBlcicsCiAgICAgICAgIG9yZGVyID0gImhjbHVzdCIsCiAgICAgICAgIGFkZHJlY3QgPSAyLAogICAgICAgICB0bC5jZXggPSAwLjQpCmBgYAoKIyMjIFNwZWNpZmljIHRvIFByZWRpY3Rpb24KIyMjIyBTY2F0dGVyIFBsb3RzIC0gTGluZWFyIC0gTm9uIExpbmVhciByZWxhdGlvbnNoaXAKYGBge3J9CmNoYXJ0LkNvcnJlbGF0aW9uKHByZWRpY3RpdmVWYXJpYWJsZXNfc2FtcGxlWywxOjE5XSwKICAgICAgICAgICAgICAgICAgaGlzdG9ncmFtID0gVFJVRSwgcGNoID0gMTkpCmBgYAojIyMjIENvcnJlbGF0aW9uIE1hdHJpeCB3aXRoIElzQ2FuY2VsZWQKYGBge3J9CmhpZ2hjb3JyZWxhdGlvbiA8LSByYXdfZGF0YV9lbmNvZGVkCmhpZ2hjb3JyZWxhdGlvbiRJc0NhbmNlbGVkIDwtIGFzLm51bWVyaWMoaGlnaGNvcnJlbGF0aW9uJElzQ2FuY2VsZWQpCgpjb3JycGxvdChjb3IoaGlnaGNvcnJlbGF0aW9uWywxOjIxXSwKICAgICAgICAgICAgIHVzZSA9ICJjb21wbGV0ZS5vYnMiKSwKICAgICAgICAgbWV0aG9kID0gImNpcmNsZSIsCiAgICAgICAgIHR5cGUgPSAndXBwZXInLAogICAgICAgICBvcmRlciA9ICJoY2x1c3QiLAogICAgICAgICBhZGRyZWN0ID0gMiwKICAgICAgICAgdGwuY2V4ID0gMC40KQpgYGAKIyMjIyBIaWdoZXN0IExpbmVhciBDb3JyZWxhdGlvbiB3aXRoIElzQ2FuY2VsZWQKYGBge3J9CiNDaGVjayBpbml0aWFsIGNvcnJlbGF0aW9uIGJldHdlZW4gdmFyaWFibGVzIGFuZCBJc0NhbmNlbGVkIGJhc2VkIG9uIGEgY29lZmZpY2llbnQuCgpuZXdEYXRhLmNvciA9IGNvcihoaWdoY29ycmVsYXRpb25bLDE6MjFdLAogICAgICAgICAgICAgdXNlID0gImNvbXBsZXRlLm9icyIpCmNvcnJwbG90KG5ld0RhdGEuY29yKQpjb3JycGxvdChuZXdEYXRhLmNvciwgdHlwZSA9ICJ1cHBlciIsIG9yZGVyID0gImhjbHVzdCIsIHRsLmNvbCA9ICJibGFjayIsIHRsLnNydCA9IDQ1KQoKCgpuZXdEYXRhLmNvciA8LSBkYXRhLmZyYW1lKG5ld0RhdGEuY29yKQpjb3JyZWxhdGVkIDwtIGMoKQp2YXJpYWJsZSA8LSBjKCkKY29lZmZpY2llbnRfbGltaXQgIDwtIGMoKQpzZWxlY3RfY29sdW1ucyA8LSAgYygpCmNvZWZmIDwtICAwLjEKCgpmb3IoY29sdW1uIGluIDE6ZGltKG5ld0RhdGEuY29yKVsxXSl7CiAgaWYoYWJzKG5ld0RhdGEuY29yWzEsY29sdW1uXSkgPiBjb2VmZil7CiAgICB2YXJpYWJsZSA8LSAgYXBwZW5kKHZhcmlhYmxlLCBuYW1lcyhuZXdEYXRhLmNvcilbY29sdW1uXSkKICAgIGNvcnJlbGF0ZWQgIDwtICBhcHBlbmQoY29ycmVsYXRlZCwgbmV3RGF0YS5jb3JbMSxjb2x1bW5dICkKICAgIGNvZWZmaWNpZW50X2xpbWl0IDwtIGFwcGVuZChjb2VmZmljaWVudF9saW1pdCxjb2VmZikKICB9Cn0KY29ycmVsYXRlZF92YXJpYWJsZXMgPC0gIGRhdGEuZnJhbWUodmFyaWFibGUsY29ycmVsYXRlZCwgY29lZmZpY2llbnRfbGltaXQpCiMgY29ycmVsYXRlZF92YXJpYWJsZXMgPC0gIGRhdGEuZnJhbWUodmFyaWFibGUsY29ycmVsYXRlZCkKCnByaW50KGNvcnJlbGF0ZWRfdmFyaWFibGVzKQpgYGAKCiMjIyMgQ29udGluZ2VuY3kgVGFibGUgb2YgQ2F0ZWdvcmljYWwgVmFyaWFibGVzCmBgYHtyfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDT05USU5HRU5DWSBUQUJMRSBPRiBDQVRFR09SSUNBTCBWQVJJQUJMRVMjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBJc0NhbmNlbGVkIFZTIEFycml2YWxEYXRlTW9udGgjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyB0d28td2F5IGNvbnRpbmdlbmN5IHRhYmxlIG9mIGNhdGVnb3JpY2FsIG91dGNvbWUgYW5kIHByZWRpY3RvcnMKCmNvbnRpbmdlbmN5X3RhYmxlIDwtIHh0YWJzKH5Jc0NhbmNlbGVkK0Fycml2YWxEYXRlTW9udGgsIGRhdGEgPSByYXdfZGF0YSkKY29udGluZ2VuY3lfdGFibGVfcGVyY2VudGFnZSA8LSBjKCkKQXJyaXZhbERhdGVNb250aCA8LSAwCmZvciggQXJyaXZhbERhdGVNb250aCBpbiAxOmRpbShjb250aW5nZW5jeV90YWJsZSlbMl0pewogIGNvbnRpbmdlbmN5X3RhYmxlX3BlcmNlbnRhZ2VbQXJyaXZhbERhdGVNb250aF0gPSBjb250aW5nZW5jeV90YWJsZVsyLCBBcnJpdmFsRGF0ZU1vbnRoXS9jb250aW5nZW5jeV90YWJsZVsxLCBBcnJpdmFsRGF0ZU1vbnRoXSoxMDAKfQoKYmFycGxvdChjb250aW5nZW5jeV90YWJsZV9wZXJjZW50YWdlLAogICAgICAgIG1haW4gPSAiSXNDYW5jZWxlZCAlIHZzIEFycml2YWxEYXRlTW9udGgiLAogICAgICAgIHhsYWIgPSAiQXJyaXZhbERhdGVNb250aCIsCiAgICAgICAgeWxhYiA9ICJJc0NhbmNlbGVkICUiLAogICAgICAgIG5hbWVzLmFyZyA9IGMoIkFwcmlsIiwgIkF1Z3VzdCIsIkRlY2VtYmVyIiwiRmVicnVhcnkiLCJKYW51YXJ5IiwgIkp1bHkiLCJKdW5lIiwgIk1hcmNoIiwgIk1heSIsICJOb3ZlbWJlciIsICAiT2N0b2JlciIsICJTZXB0ZW1iZXIiKSwKICAgICAgICBjb2wgPSAiZGFya3JlZCIsCiAgICAgICAgaG9yaXogPSBGQUxTRSkKCmRlZmF1bHRfdG90YWwgPC0gc3VtKGNvbnRpbmdlbmN5X3RhYmxlWzIsXSkKYGBgCgpgYGB7cn0KIyBJc0NhbmNlbGVkIFZTIE1lYWwjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyB0d28td2F5IGNvbnRpbmdlbmN5IHRhYmxlIG9mIGNhdGVnb3JpY2FsIG91dGNvbWUgYW5kIHByZWRpY3RvcnMKCmNvbnRpbmdlbmN5X3RhYmxlIDwtIHh0YWJzKH5Jc0NhbmNlbGVkK01lYWwsIGRhdGEgPSByYXdfZGF0YSkKY29udGluZ2VuY3lfdGFibGVfcGVyY2VudGFnZSA8LSBjKCkKTWVhbCA8LSAwCmZvciggTWVhbCBpbiAxOmRpbShjb250aW5nZW5jeV90YWJsZSlbMl0pewogIGNvbnRpbmdlbmN5X3RhYmxlX3BlcmNlbnRhZ2VbTWVhbF0gPSBjb250aW5nZW5jeV90YWJsZVsyLCBNZWFsXS9jb250aW5nZW5jeV90YWJsZVsxLCBNZWFsXSoxMDAKfQoKYmFycGxvdChjb250aW5nZW5jeV90YWJsZV9wZXJjZW50YWdlLAogICAgICAgIG1haW4gPSAiSXNDYW5jZWxlZCAlIHZzIE1lYWwiLAogICAgICAgIHhsYWIgPSAiTWVhbCIsCiAgICAgICAgeWxhYiA9ICJJc0NhbmNlbGVkICUiLAogICAgICAgIG5hbWVzLmFyZyA9IGMoIkJCIiwgIkZCIiwgIkhCIiwgIlNDIiwgIlVuZGVmaW5lZCIpLAogICAgICAgIGNvbCA9ICJkYXJrcmVkIiwKICAgICAgICBob3JpeiA9IEZBTFNFKQoKZGVmYXVsdF90b3RhbCA8LSBzdW0oY29udGluZ2VuY3lfdGFibGVbMixdKQoKYGBgCgpgYGB7cn0KIyBJc0NhbmNlbGVkIFZTIE1hcmtldFNlZ21lbnQjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyB0d28td2F5IGNvbnRpbmdlbmN5IHRhYmxlIG9mIGNhdGVnb3JpY2FsIG91dGNvbWUgYW5kIHByZWRpY3RvcnMKCmNvbnRpbmdlbmN5X3RhYmxlIDwtIHh0YWJzKH5Jc0NhbmNlbGVkK01hcmtldFNlZ21lbnQsIGRhdGEgPSByYXdfZGF0YSkKY29udGluZ2VuY3lfdGFibGVfcGVyY2VudGFnZSA8LSBjKCkKTWFya2V0U2VnbWVudCA8LSAwCmZvciggTWFya2V0U2VnbWVudCBpbiAxOmRpbShjb250aW5nZW5jeV90YWJsZSlbMl0pewogIGNvbnRpbmdlbmN5X3RhYmxlX3BlcmNlbnRhZ2VbTWFya2V0U2VnbWVudF0gPSBjb250aW5nZW5jeV90YWJsZVsyLCBNYXJrZXRTZWdtZW50XS9jb250aW5nZW5jeV90YWJsZVsxLCBNYXJrZXRTZWdtZW50XSoxMDAKfQoKYmFycGxvdChjb250aW5nZW5jeV90YWJsZV9wZXJjZW50YWdlLAogICAgICAgIG1haW4gPSAiSXNDYW5jZWxlZCAlIHZzIE1hcmtldFNlZ21lbnQiLAogICAgICAgIHhsYWIgPSAiTWFya2V0U2VnbWVudCIsCiAgICAgICAgeWxhYiA9ICJJc0NhbmNlbGVkICUiLAogICAgICAgIG5hbWVzLmFyZyA9IGMoIkNvbXBsZW1lbnRhcnkiLCAiQ29ycG9yYXRlIiwiRGlyZWN0IiwiR3JvdXBzIiwgIk9mZmxpbmUgVEEvVE8iLCAiT25saW5lIFRBIiksCiAgICAgICAgY29sID0gImRhcmtyZWQiLAogICAgICAgIGhvcml6ID0gRkFMU0UpCgpkZWZhdWx0X3RvdGFsIDwtIHN1bShjb250aW5nZW5jeV90YWJsZVsyLF0pCmBgYApgYGB7cn0KIyBJc0NhbmNlbGVkIFZTIERpc3RyaWJ1dGlvbkNoYW5uZWwjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyB0d28td2F5IGNvbnRpbmdlbmN5IHRhYmxlIG9mIGNhdGVnb3JpY2FsIG91dGNvbWUgYW5kIHByZWRpY3RvcnMKCmNvbnRpbmdlbmN5X3RhYmxlIDwtIHh0YWJzKH5Jc0NhbmNlbGVkK0Rpc3RyaWJ1dGlvbkNoYW5uZWwsIGRhdGEgPSByYXdfZGF0YSkKY29udGluZ2VuY3lfdGFibGVfcGVyY2VudGFnZSA8LSBjKCkKRGlzdHJpYnV0aW9uQ2hhbm5lbCA8LSAwCmZvciggRGlzdHJpYnV0aW9uQ2hhbm5lbCBpbiAxOmRpbShjb250aW5nZW5jeV90YWJsZSlbMl0pewogIGNvbnRpbmdlbmN5X3RhYmxlX3BlcmNlbnRhZ2VbRGlzdHJpYnV0aW9uQ2hhbm5lbF0gPSBjb250aW5nZW5jeV90YWJsZVsyLCBEaXN0cmlidXRpb25DaGFubmVsXS9jb250aW5nZW5jeV90YWJsZVsxLCBEaXN0cmlidXRpb25DaGFubmVsXSoxMDAKfQoKYmFycGxvdChjb250aW5nZW5jeV90YWJsZV9wZXJjZW50YWdlLAogICAgICAgIG1haW4gPSAiSXNDYW5jZWxlZCAlIHZzIERpc3RyaWJ1dGlvbkNoYW5uZWwiLAogICAgICAgIHhsYWIgPSAiRGlzdHJpYnV0aW9uQ2hhbm5lbCIsCiAgICAgICAgeWxhYiA9ICJJc0NhbmNlbGVkICUiLAogICAgICAgIG5hbWVzLmFyZyA9IGMoIkNvcnBvcmF0ZSIsICJEaXJlY3QiLCAiVEEvVE8iLCAiVW5kZWZpbmVkIiksCiAgICAgICAgY29sID0gImRhcmtyZWQiLAogICAgICAgIGhvcml6ID0gRkFMU0UpCgpkZWZhdWx0X3RvdGFsIDwtIHN1bShjb250aW5nZW5jeV90YWJsZVsyLF0pCmBgYApgYGB7cn0KIyBJc0NhbmNlbGVkIFZTIFJlc2VydmVkUm9vbVR5cGUjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyB0d28td2F5IGNvbnRpbmdlbmN5IHRhYmxlIG9mIGNhdGVnb3JpY2FsIG91dGNvbWUgYW5kIHByZWRpY3RvcnMKCmNvbnRpbmdlbmN5X3RhYmxlIDwtIHh0YWJzKH5Jc0NhbmNlbGVkK1Jlc2VydmVkUm9vbVR5cGUsIGRhdGEgPSByYXdfZGF0YSkKY29udGluZ2VuY3lfdGFibGVfcGVyY2VudGFnZSA8LSBjKCkKUmVzZXJ2ZWRSb29tVHlwZSA8LSAwCmZvciggUmVzZXJ2ZWRSb29tVHlwZSBpbiAxOmRpbShjb250aW5nZW5jeV90YWJsZSlbMl0pewogIGNvbnRpbmdlbmN5X3RhYmxlX3BlcmNlbnRhZ2VbUmVzZXJ2ZWRSb29tVHlwZV0gPSBjb250aW5nZW5jeV90YWJsZVsyLCBSZXNlcnZlZFJvb21UeXBlXS9jb250aW5nZW5jeV90YWJsZVsxLCBSZXNlcnZlZFJvb21UeXBlXSoxMDAKfQpjb250aW5nZW5jeV90YWJsZQoKYmFycGxvdChjb250aW5nZW5jeV90YWJsZV9wZXJjZW50YWdlWy0xMF0sCiAgICAgICAgbWFpbiA9ICJJc0NhbmNlbGVkICUgdnMgUmVzZXJ2ZWRSb29tVHlwZSIsCiAgICAgICAgeGxhYiA9ICJSZXNlcnZlZFJvb21UeXBlIiwKICAgICAgICB5bGFiID0gIklzQ2FuY2VsZWQgJSIsCiAgICAgICAgbmFtZXMuYXJnID0gYygiQSIsICJCIiwgIkMiLCAiRCIsIkUiLCJGIiwiRyIsIkgiLCJMIiksCiAgICAgICAgY29sID0gImRhcmtyZWQiLAogICAgICAgIGhvcml6ID0gRkFMU0UpCmRlZmF1bHRfdG90YWwgPC0gc3VtKGNvbnRpbmdlbmN5X3RhYmxlWzIsXSkKYGBgCgpgYGB7cn0KIyBJc0NhbmNlbGVkIFZTIEFzc2lnbmVkUm9vbVR5cGUjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyB0d28td2F5IGNvbnRpbmdlbmN5IHRhYmxlIG9mIGNhdGVnb3JpY2FsIG91dGNvbWUgYW5kIHByZWRpY3RvcnMKCmNvbnRpbmdlbmN5X3RhYmxlIDwtIHh0YWJzKH5Jc0NhbmNlbGVkK0Fzc2lnbmVkUm9vbVR5cGUsIGRhdGEgPSByYXdfZGF0YSkKY29udGluZ2VuY3lfdGFibGVfcGVyY2VudGFnZSA8LSBjKCkKQXNzaWduZWRSb29tVHlwZSA8LSAwCmZvciggQXNzaWduZWRSb29tVHlwZSBpbiAxOmRpbShjb250aW5nZW5jeV90YWJsZSlbMl0pewogIGNvbnRpbmdlbmN5X3RhYmxlX3BlcmNlbnRhZ2VbQXNzaWduZWRSb29tVHlwZV0gPSBjb250aW5nZW5jeV90YWJsZVsyLCBBc3NpZ25lZFJvb21UeXBlXS9jb250aW5nZW5jeV90YWJsZVsxLCBBc3NpZ25lZFJvb21UeXBlXSoxMDAKfQoKYmFycGxvdChjb250aW5nZW5jeV90YWJsZV9wZXJjZW50YWdlWy1jKDEwLDExKV0sCiAgICAgICAgbWFpbiA9ICJJc0NhbmNlbGVkICUgdnMgQXNzaWduZWRSb29tVHlwZSIsCiAgICAgICAgeGxhYiA9ICJBc3NpZ25lZFJvb21UeXBlIiwKICAgICAgICB5bGFiID0gIklzQ2FuY2VsZWQgJSIsCiAgICAgICAgbmFtZXMuYXJnID0gYygiQSIsICJCIiwgIkMiLCAiRCIsIkUiLCJGIiwiRyIsIkgiLCJJIiksCiAgICAgICAgY29sID0gImRhcmtyZWQiLAogICAgICAgIGhvcml6ID0gRkFMU0UpCmRlZmF1bHRfdG90YWwgPC0gc3VtKGNvbnRpbmdlbmN5X3RhYmxlWzIsXSkKYGBgCmBgYHtyfQojIElzQ2FuY2VsZWQgVlMgRGVwb3NpdFR5cGUjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyB0d28td2F5IGNvbnRpbmdlbmN5IHRhYmxlIG9mIGNhdGVnb3JpY2FsIG91dGNvbWUgYW5kIHByZWRpY3RvcnMKCmNvbnRpbmdlbmN5X3RhYmxlIDwtIHh0YWJzKH5Jc0NhbmNlbGVkK0RlcG9zaXRUeXBlLCBkYXRhID0gcmF3X2RhdGEpCmNvbnRpbmdlbmN5X3RhYmxlX3BlcmNlbnRhZ2UgPC0gYygpCkRlcG9zaXRUeXBlIDwtIDAKZm9yKCBEZXBvc2l0VHlwZSBpbiAxOmRpbShjb250aW5nZW5jeV90YWJsZSlbMl0pewogIGNvbnRpbmdlbmN5X3RhYmxlX3BlcmNlbnRhZ2VbRGVwb3NpdFR5cGVdID0gY29udGluZ2VuY3lfdGFibGVbMiwgRGVwb3NpdFR5cGVdL2NvbnRpbmdlbmN5X3RhYmxlWzEsIERlcG9zaXRUeXBlXSoxMDAKfQoKYmFycGxvdChjb250aW5nZW5jeV90YWJsZV9wZXJjZW50YWdlLAogICAgICAgIG1haW4gPSAiSXNDYW5jZWxlZCAlIHZzIERlcG9zaXRUeXBlIiwKICAgICAgICB4bGFiID0gIkRlcG9zaXRUeXBlIiwKICAgICAgICB5bGFiID0gIklzQ2FuY2VsZWQgJSIsCiAgICAgICAgbmFtZXMuYXJnID0gYygiTm8gRGVwb3NpdCIsICJOb24gUmVmdW5kIiwgIlJlZnVuZGFibGUiKSwKICAgICAgICBjb2wgPSAiZGFya3JlZCIsCiAgICAgICAgaG9yaXogPSBGQUxTRSkKZGVmYXVsdF90b3RhbCA8LSBzdW0oY29udGluZ2VuY3lfdGFibGVbMixdKQpgYGAKCmBgYHtyfQojIElzQ2FuY2VsZWQgVlMgQ3VzdG9tZXJUeXBlIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMgdHdvLXdheSBjb250aW5nZW5jeSB0YWJsZSBvZiBjYXRlZ29yaWNhbCBvdXRjb21lIGFuZCBwcmVkaWN0b3JzCgpjb250aW5nZW5jeV90YWJsZSA8LSB4dGFicyh+SXNDYW5jZWxlZCtDdXN0b21lclR5cGUsIGRhdGEgPSByYXdfZGF0YSkKY29udGluZ2VuY3lfdGFibGVfcGVyY2VudGFnZSA8LSBjKCkKQ3VzdG9tZXJUeXBlIDwtIDAKZm9yKCBDdXN0b21lclR5cGUgaW4gMTpkaW0oY29udGluZ2VuY3lfdGFibGUpWzJdKXsKICBjb250aW5nZW5jeV90YWJsZV9wZXJjZW50YWdlW0N1c3RvbWVyVHlwZV0gPSBjb250aW5nZW5jeV90YWJsZVsyLCBDdXN0b21lclR5cGVdL2NvbnRpbmdlbmN5X3RhYmxlWzEsIEN1c3RvbWVyVHlwZV0qMTAwCn0KCmJhcnBsb3QoY29udGluZ2VuY3lfdGFibGVfcGVyY2VudGFnZSwKICAgICAgICBtYWluID0gIklzQ2FuY2VsZWQgJSB2cyBDdXN0b21lclR5cGUiLAogICAgICAgIHhsYWIgPSAiQ3VzdG9tZXJUeXBlIiwKICAgICAgICB5bGFiID0gIklzQ2FuY2VsZWQgJSIsCiAgICAgICAgbmFtZXMuYXJnID0gYygiQ29udHJhY3QiLCAiR3JvdXAiLCAiVHJhbnNpZW50IiwgIlRyYW5zaWVudC1QYXJ0eSIpLAogICAgICAgIGNvbCA9ICJkYXJrcmVkIiwKICAgICAgICBob3JpeiA9IEZBTFNFKQpkZWZhdWx0X3RvdGFsIDwtIHN1bShjb250aW5nZW5jeV90YWJsZVsyLF0pCmBgYApgYGB7cn0KIyBJc0NhbmNlbGVkIFZTIENvbXBhbnlZZXMjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyB0d28td2F5IGNvbnRpbmdlbmN5IHRhYmxlIG9mIGNhdGVnb3JpY2FsIG91dGNvbWUgYW5kIHByZWRpY3RvcnMKCmNvbnRpbmdlbmN5X3RhYmxlIDwtIHh0YWJzKH5Jc0NhbmNlbGVkK0NvbXBhbnlZZXMsIGRhdGEgPSByYXdfZGF0YV9lbmNvZGVkKQpjb250aW5nZW5jeV90YWJsZV9wZXJjZW50YWdlIDwtIGMoKQpDb21wYW55WWVzIDwtIDAKZm9yKCBDb21wYW55WWVzIGluIDE6ZGltKGNvbnRpbmdlbmN5X3RhYmxlKVsyXSl7CiAgY29udGluZ2VuY3lfdGFibGVfcGVyY2VudGFnZVtDb21wYW55WWVzXSA9IGNvbnRpbmdlbmN5X3RhYmxlWzIsIENvbXBhbnlZZXNdL2NvbnRpbmdlbmN5X3RhYmxlWzEsIENvbXBhbnlZZXNdKjEwMAp9CgpiYXJwbG90KGNvbnRpbmdlbmN5X3RhYmxlX3BlcmNlbnRhZ2UsCiAgICAgICAgbWFpbiA9ICJJc0NhbmNlbGVkICUgdnMgQ29tcGFueVllcyIsCiAgICAgICAgeGxhYiA9ICJDb21wYW55WWVzIiwKICAgICAgICB5bGFiID0gIklzQ2FuY2VsZWQgJSIsCiAgICAgICAgbmFtZXMuYXJnID0gYygwLCAxKSwKICAgICAgICBjb2wgPSAiZGFya3JlZCIsCiAgICAgICAgaG9yaXogPSBGQUxTRSkKZGVmYXVsdF90b3RhbCA8LSBzdW0oY29udGluZ2VuY3lfdGFibGVbMixdKQpgYGAKYGBge3J9CiMgSXNDYW5jZWxlZCBWUyBBZ2VudFllcyMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjIHR3by13YXkgY29udGluZ2VuY3kgdGFibGUgb2YgY2F0ZWdvcmljYWwgb3V0Y29tZSBhbmQgcHJlZGljdG9ycwoKY29udGluZ2VuY3lfdGFibGUgPC0geHRhYnMofklzQ2FuY2VsZWQrQWdlbnRZZXMsIGRhdGEgPSByYXdfZGF0YV9lbmNvZGVkKQpjb250aW5nZW5jeV90YWJsZV9wZXJjZW50YWdlIDwtIGMoKQpBZ2VudFllcyA8LSAwCmZvciggQWdlbnRZZXMgaW4gMTpkaW0oY29udGluZ2VuY3lfdGFibGUpWzJdKXsKICBjb250aW5nZW5jeV90YWJsZV9wZXJjZW50YWdlW0FnZW50WWVzXSA9IGNvbnRpbmdlbmN5X3RhYmxlWzIsIEFnZW50WWVzXS9jb250aW5nZW5jeV90YWJsZVsxLCBBZ2VudFllc10qMTAwCn0KCmJhcnBsb3QoY29udGluZ2VuY3lfdGFibGVfcGVyY2VudGFnZSwKICAgICAgICBtYWluID0gIklzQ2FuY2VsZWQgJSB2cyBBZ2VudFllcyIsCiAgICAgICAgeGxhYiA9ICJBZ2VudFllcyIsCiAgICAgICAgeWxhYiA9ICJJc0NhbmNlbGVkICUiLAogICAgICAgIG5hbWVzLmFyZyA9IGMoMCwgMSksCiAgICAgICAgY29sID0gImRhcmtyZWQiLAogICAgICAgIGhvcml6ID0gRkFMU0UpCmRlZmF1bHRfdG90YWwgPC0gc3VtKGNvbnRpbmdlbmN5X3RhYmxlWzIsXSkKYGBgCiMgQ0xVU1RFUklORwpDbHVzdGVyaW5nOgotIEdvb2RuZXNzIG9mIHRoZSBjbHVzdGVyIEFuYWx5c2lzCi0gT3B0aW1hbCBOdW1iZXIgb2YgQ2x1c3RlcnMKIENsdXN0ZXJpbmcKCgojIyBEQVRBIFBSRS1QUk9DRVNTSU5HCiMjIyBTQ0FMSU5HCmBgYHtyfQojU2NhbGVkIHNhbXBsZQpwcmVkaWN0aXZlVmFyaWFibGVzX3NhbXBsZVNjYWxlZCA9IHNjYWxlKHByZWRpY3RpdmVWYXJpYWJsZXNfc2FtcGxlKQpwcmVkaWN0aXZlVmFyaWFibGVzX3NhbXBsZVNjYWxlZCA9IGFzLmRhdGEuZnJhbWUocHJlZGljdGl2ZVZhcmlhYmxlc19zYW1wbGVTY2FsZWQpCmBgYAoKIyMjIFZBUklBQkxFUyBXSVRIIFpFUk8gVkFMVUVTIElOIEFMTCBDT0xVTU5TCmBgYHtyfQojQ29sdW1ucyB3aXRoIGFsbCB6ZXJvcyBmcm9tIHRoZSBzYW1wbGUKcHJlZGljdGl2ZVZhcmlhYmxlc05vblplcm8gPSBwcmVkaWN0aXZlVmFyaWFibGVzX3NhbXBsZVshc2FwcGx5KHByZWRpY3RpdmVWYXJpYWJsZXNfc2FtcGxlLCBmdW5jdGlvbih4KSBzdW0oeCk9PSAwKV0KcHJlZGljdGl2ZVZhcmlhYmxlc05vblplcm9TY2FsZWQgPSBzY2FsZShwcmVkaWN0aXZlVmFyaWFibGVzTm9uWmVybykKYGBgCgojIyMgR09XRVIKYGBge3J9CmxpYnJhcnkoY2x1c3RlcikKZ293ZXJfZGlzdCA9IGRhaXN5KGFzLm1hdHJpeChwcmVkaWN0aXZlVmFyaWFibGVzX3NhbXBsZSkgLCBtZXRyaWMgPSAiZXVjbGlkZWFuIiwgc3RhbmQgPSBGQUxTRSkKCmBgYAoKCiMjIEdPT0RORVNTIE9GIFRIRSBDTFVTVEVSIEFOQUxZU0lTCmBgYHtyfQpib25kYWRfYWMgPSBnZXRfY2x1c3RfdGVuZGVuY3kocHJlZGljdGl2ZVZhcmlhYmxlc19zYW1wbGUsIDUwMCkKYm9uZGFkX2FjJGhvcGtpbnNfc3RhdApgYGAKCiMjIE9QVElNQUwgTlVNQkVSIE9GIENMVVNURVJTCgojIyMgTlVNQkVSIEZPUiBOT04gSElFUkFSQ0hJQ0FMIENMVVNURVJJTkcKCiMjIyMgR0VORVJBTCBGT1IgQUxMIE9GIFRIRU0KYGBge3J9CmNsdXMubmIgPSBOYkNsdXN0KHByZWRpY3RpdmVWYXJpYWJsZXNOb25aZXJvU2NhbGVkLCBkaXN0YW5jZSA9ICJldWNsaWRlYW4iLG1pbi5uYyA9IDIsIG1heC5uYyA9IDEwLG1ldGhvZCA9ICJjb21wbGV0ZSIsIGluZGV4ID0iZ2FwIikKCmNsdXMubmIkQmVzdC5uYwpgYGAKCgojIyMjIFBBTQoKYGBge3J9CiMgQ2FsY3VsYXRlIHNpbGhvdWV0dGUgd2lkdGggZm9yIG1hbnkgayB1c2luZyBQQU0Kc2lsX3dpZHRoIDwtIGMoTkEpCmZvcihpIGluIDI6MTApewogIHBhbV9maXQgPC0gcGFtKGdvd2VyX2Rpc3QsCiAgICAgICAgICAgICAgICAgZGlzcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgayA9IGkpCiAgc2lsX3dpZHRoW2ldIDwtIHBhbV9maXQkc2lsaW5mbyRhdmcud2lkdGgKfQojIFBsb3Qgc2lob3VldHRlIHdpZHRoIChoaWdoZXIgaXMgYmV0dGVyKQpwbG90KDE6MTAsIHNpbF93aWR0aCwKICAgICB4bGFiID0gIk9wdGltYWwgayBjbHVzdGVycyIsCiAgICAgeWxhYiA9ICJBdmVyYWdlIFdpZHRoIFByb2ZpbGUiKQpsaW5lcygxOjEwLCBzaWxfd2lkdGgpCmBgYAoKCiMjIyMgQ0xBUkEKCgpgYGB7cn0KIyBPbmx5IG51bWVyaWMgVmFyaWFibGVzIGZvciBjbHVzdGVyaW5nCm51bWVyaWNWYXJpYWJsZXMgPC0gcHJlZGljdGl2ZVZhcmlhYmxlc05vblplcm9bYygxNyw3LDIsMyw5LDEzLDgsMTYsMSwxMiwxMSwxOCw1LDYsMTkpXQpgYGAKCmBgYHtyfQpmdml6X25iY2x1c3QobnVtZXJpY1ZhcmlhYmxlcywgY2x1c3Rlcjo6Y2xhcmEsIG1ldGhvZCA9ICJzaWxob3VldHRlIikgKyBnZ3RpdGxlKCJPcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycyAtIENMQVJBIikgKyBsYWJzKHggPSAiT3B0aW1hbCBrIGNsdXN0ZXJzIiwgeSA9ICJBdmVyYWdlIFdpZHRoIFByb2ZpbGUiKQojIDIgZ3JvdXBzCmBgYApUaGUgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMgd2lsbCBiZSBnaXZlbiBieSB0aGUgdmFsdWUgb2YgayB0aGF0IG1heGltaXplcyB0aGUgYXZlcmFnZSBwcm9maWxlLiBJbiB0aGlzIGNhc2U6IDIKCgojIyMjIEZVWlpZIEFOQUxZU0lTIENMVVNURVJJTkcKCgpgYGB7cn0KIyBDYWxjdWxhdGUgc2lsaG91ZXR0ZSB3aWR0aCBmb3IgbWFueSBrIHVzaW5nIApzaWxfd2lkdGggPC0gYyhOQSkKZm9yKGkgaW4gMjoxMCl7CiBmYW5ueV9maXQgPC0gZmFubnkoZ293ZXJfZGlzdCwKICAgICAgICAgICAgICAgICBkaXNzID0gVFJVRSwKICAgICAgICAgICAgICAgICBrID0gaSkKICBzaWxfd2lkdGhbaV0gPC0gZmFubnlfZml0JHNpbGluZm8kYXZnLndpZHRoCn0KIyBQbG90IHNpaG91ZXR0ZSB3aWR0aCAoaGlnaGVyIGlzIGJldHRlcikKcGxvdCgxOjEwLCBzaWxfd2lkdGgsCiAgICAgeGxhYiA9ICJOdW1iZXIgb2YgY2x1c3RlcnMiLAogICAgIHlsYWIgPSAiU2lsaG91ZXR0ZSBXaWR0aCIpCmxpbmVzKDE6MTAsIHNpbF93aWR0aCkKYGBgCgojIyMgTlVNQkVSIEZPUiBISUVSQVJDSElDQUwgQ0xVU1RFUklORwoKIyMjIyBIQ1VUCmBgYHtyfQphZ2dsLmNsdXN0LmMgPC0gaGNsdXN0KGdvd2VyX2Rpc3QsIG1ldGhvZCA9ICJjb21wbGV0ZSIpIAoKCmxpYnJhcnkoZnBjKQpjc3RhdHMudGFibGUgPC0gZnVuY3Rpb24oZGlzdCwgdHJlZSwgaykgewogIGNsdXN0LmFzc2VzcyA8LSBjKCJjbHVzdGVyLm51bWJlciIsIm4iLCJ3aXRoaW4uY2x1c3Rlci5zcyIsImF2ZXJhZ2Uud2l0aGluIiwiYXZlcmFnZS5iZXR3ZWVuIiwid2IucmF0aW8iLCJkdW5uMiIsImF2Zy5zaWx3aWR0aCIpCiAgY2x1c3Quc2l6ZSA8LSBjKCJjbHVzdGVyLnNpemUiKQogIHN0YXRzLm5hbWVzIDwtIGMoKQogIHJvdy5jbHVzdCA8LSBjKCkKICBvdXRwdXQuc3RhdHMgPC0gbWF0cml4KG5jb2wgPSBrLCBucm93ID0gbGVuZ3RoKGNsdXN0LmFzc2VzcykpCiAgY2x1c3Rlci5zaXplcyA8LSBtYXRyaXgobmNvbCA9IGssIG5yb3cgPSBrKQogIGZvciAoaSBpbiBjKDE6aykpIHsKICAgIHJvdy5jbHVzdFtpXSA8LSBwYXN0ZSgiQ2x1c3Rlci0iLCBpLCAiIHNpemUiKQogIH0KICBmb3IgKGkgaW4gYygyOmspKSB7CiAgICBzdGF0cy5uYW1lc1tpXSA8LSBwYXN0ZSgiVGVzdCIsIGkgLSAxKQogICAgCiAgICBmb3IgKGogaW4gc2VxX2Fsb25nKGNsdXN0LmFzc2VzcykpIHsKICAgICAgb3V0cHV0LnN0YXRzW2osIGldIDwtIHVubGlzdChjbHVzdGVyLnN0YXRzKGQgPSBkaXN0LCBjbHVzdGVyaW5nID0gY3V0cmVlKHRyZWUsIGsgPSBpKSlbY2x1c3QuYXNzZXNzXSlbal0KICAgICAgCiAgICB9CiAgICAKICAgIGZvciAoZCBpbiAxOmspIHsKICAgICAgY2x1c3Rlci5zaXplc1tkLCBpXSA8LSB1bmxpc3QoY2x1c3Rlci5zdGF0cyhkID0gZGlzdCwgY2x1c3RlcmluZyA9IGN1dHJlZSh0cmVlLCBrID0gaSkpW2NsdXN0LnNpemVdKVtkXQogICAgICBkaW0oY2x1c3Rlci5zaXplc1tkLCBpXSkgPC0gYyhsZW5ndGgoY2x1c3Rlci5zaXplc1tpXSksIDEpCiAgICAgIGNsdXN0ZXIuc2l6ZXNbZCwgaV0KICAgICAgCiAgICB9CiAgfQogIG91dHB1dC5zdGF0cy5kZiA8LSBkYXRhLmZyYW1lKG91dHB1dC5zdGF0cykKICBjbHVzdGVyLnNpemVzIDwtIGRhdGEuZnJhbWUoY2x1c3Rlci5zaXplcykKICBjbHVzdGVyLnNpemVzW2lzLm5hKGNsdXN0ZXIuc2l6ZXMpXSA8LSAwCiAgcm93cy5hbGwgPC0gYyhjbHVzdC5hc3Nlc3MsIHJvdy5jbHVzdCkKICAKICAjIHJvd25hbWVzKG91dHB1dC5zdGF0cy5kZikgPC0gY2x1c3QuYXNzZXNzCiAgb3V0cHV0IDwtIHJiaW5kKG91dHB1dC5zdGF0cy5kZiwgY2x1c3Rlci5zaXplcylbICwtMV0KICBjb2xuYW1lcyhvdXRwdXQpIDwtIHN0YXRzLm5hbWVzWzI6a10KICByb3duYW1lcyhvdXRwdXQpIDwtIHJvd3MuYWxsCiAgaXMubnVtIDwtIHNhcHBseShvdXRwdXQsIGlzLm51bWVyaWMpCiAgb3V0cHV0W2lzLm51bV0gPC0gbGFwcGx5KG91dHB1dFtpcy5udW1dLCByb3VuZCwgMikKICBvdXRwdXQKfQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRhdGEuZnJhbWUodChjc3RhdHMudGFibGUoZ293ZXJfZGlzdCwgYWdnbC5jbHVzdC5jLCAxNSkpKSwgCiAgICAgICBhZXMoeCA9IGNsdXN0ZXIubnVtYmVyLCB5ID0gYXZnLnNpbHdpZHRoKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKCkgKwogIGdndGl0bGUoIk9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzIC0gSENVVCIpICsKICBsYWJzKHggPSAiT3B0aW1hbCBrIGNsdXN0ZXJzIiwgeSA9ICJBdmVyYWdlIFdpZHRoIFByb2ZpbGUiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCmBgYAoKIyMgSElFUkFSQ0hJQ0FMIENMVVNURVJJTkcKCiMjIyAgREVORFJPR1JBTQoKYGBge3J9CnBsb3QoYWdnbC5jbHVzdC5jLCBtYWluID0gIkhDVVQgIikKcmVjdC5oY2x1c3QoYWdnbC5jbHVzdC5jLCBrID0gMiwgYm9yZGVyID0gMjozKQpgYGAKCiMjIyBDTFVTVEVSIFBMT1QKYGBge3J9CnNldC5zZWVkKDEyMykKZ3JwIDwtIGN1dHJlZShhZ2dsLmNsdXN0LmMsIGsgPSAyKQpmdml6X2NsdXN0ZXIobGlzdChkYXRhID0gcHJlZGljdGl2ZVZhcmlhYmxlc19zYW1wbGUsIGNsdXN0ZXIgPSBncnApLCBzdGFuZCA9IEZBTFNFLCBnZW9tID0gInBvaW50IiwgcG9pbnRzaXplID0gMSwgdGl0bGUgPSAiQ2x1c3RlciBQbG90IikKCmBgYAoKIyMjIENPTlRJTkdFTkNZIFRBQkxFCmBgYHtyfQoKY2x1c3QubnVtIDwtIGN1dHJlZShhZ2dsLmNsdXN0LmMsIGsgPSAyKQp0YWJsID0gcHJvcC50YWJsZSh0YWJsZShjbHVzdC5udW0saXNDYW5jZWxlZF9zYW1wbGUpKSoxMDAKdGFibApgYGAKYGBge3J9CnRhYmxbMSwyXS8odGFibFsxXSsgdGFibFsxLDJdKSoxMDAgIyAlIG9mIENhbmNlbGF0aW9ucyBvZiBHcm91cCAxCnRhYmxbMiwyXS8odGFibFsyXSt0YWJsWzIsMl0pKjEwMCAjICUgb2YgQ2FuY2VsYXRpb25zIG9mIEdyb3VwIDIKYGBgCgoKCiMjIyBTSUxIT1VFVFRFIFBMT1QKCgpgYGB7cn0KZ3JwIDwtIGN1dHJlZShhZ2dsLmNsdXN0LmMsIGsgPSAyKQojIGFnZ2wuY2x1c3QuYyA8LSBoY2x1c3QoZ293ZXJfZGlzdCwgbWV0aG9kID0gImNvbXBsZXRlIikgCnNpbF9jbCA8LSBzaWxob3VldHRlKGdycCxnb3dlcl9kaXN0LCB0aXRsZT10aXRsZShtYWluID0gJ0dvb2QnKSkKIyByb3duYW1lcyhzaWxfY2wpIDwtIHJvd25hbWVzKGdycCkKcGxvdChzaWxfY2wpCmBgYAoKCiMjIE5PTi0gSElFUkFSQ0hJQ0FMIENMVVNURVJJTkcKCiMjIyBQQU0KCiMjIyMgQ0xVU1RFUiBQTE9UCmBgYHtyfQpsaWJyYXJ5KFJ0c25lKQpwYW1fZml0IDwtIHBhbShnb3dlcl9kaXN0LCBkaXNzID0gVFJVRSwgayA9IDQpCgp0c25lX29iaiA8LSBSdHNuZShnb3dlcl9kaXN0LCBpc19kaXN0YW5jZSA9IFRSVUUpCnRzbmVfZGF0YSA8LSB0c25lX29iaiRZICU+JQogIGRhdGEuZnJhbWUoKSAlPiUKICBzZXROYW1lcyhjKCJYIiwgIlkiKSkgJT4lCiAgbXV0YXRlKGNsdXN0ZXIgPSBmYWN0b3IocGFtX2ZpdCRjbHVzdGVyaW5nKSkKZ2dwbG90KGFlcyh4ID0gWCwgeSA9IFkpLCBkYXRhID0gdHNuZV9kYXRhKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjbHVzdGVyKSkKYGBgCgojIyMjIENPTlRJTkdFTkNZIFRBQkxFCgpgYGB7cn0KcGFtX2ZpdCRjbHVzdGVyaW5nPC0gYXMuZmFjdG9yKHBhbV9maXQkY2x1c3RlcmluZykKdGFibCA9IHByb3AudGFibGUodGFibGUocGFtX2ZpdCRjbHVzdGVyaW5nLGlzQ2FuY2VsZWRfc2FtcGxlKSkqMTAwCnRhYmwKYGBgCgpgYGB7cn0KdGFibFsxLDJdLyh0YWJsWzFdKyB0YWJsWzEsMl0pKjEwMCAjICUgb2YgQ2FuY2VsYXRpb25zIG9mIEdyb3VwIDEKdGFibFsyLDJdLyh0YWJsWzJdK3RhYmxbMiwyXSkqMTAwICMgJSBvZiBDYW5jZWxhdGlvbnMgb2YgR3JvdXAgMgp0YWJsWzMsMl0vKHRhYmxbM10rdGFibFszLDJdKSoxMDAgIyAlIG9mIENhbmNlbGF0aW9ucyBvZiBHcm91cCAzCnRhYmxbNCwyXS8odGFibFs0XSt0YWJsWzQsMl0pKjEwMCAjICUgb2YgQ2FuY2VsYXRpb25zIG9mIEdyb3VwIDQKIyB0YWJsWzUsMl0vKHRhYmxbNV0rdGFibFs1LDJdKSoxMDAgIyAlIG9mIENhbmNlbGF0aW9ucyBvZiBHcm91cCA1CiMgdGFibFs2LDJdLyh0YWJsWzZdK3RhYmxbNiwyXSkqMTAwICMgJSBvZiBDYW5jZWxhdGlvbnMgb2YgR3JvdXAgNgojIHRhYmxbNywyXS8odGFibFs3XSt0YWJsWzcsMl0pKjEwMCAjICUgb2YgQ2FuY2VsYXRpb25zIG9mIEdyb3VwIDcKIyB0YWJsWzgsMl0vKHRhYmxbOF0rdGFibFs4LDJdKSoxMDAgIyAlIG9mIENhbmNlbGF0aW9ucyBvZiBHcm91cCA4CiMgdGFibFs5LDJdLyh0YWJsWzldK3RhYmxbOSwyXSkqMTAwICMgJSBvZiBDYW5jZWxhdGlvbnMgb2YgR3JvdXAgOQojIHRhYmxbMTAsMl0vKHRhYmxbMTBdK3RhYmxbMTAsMl0pKjEwMCAjICUgb2YgQ2FuY2VsYXRpb25zIG9mIEdyb3VwIDEwCmBgYAojIyMjIFNJTEhPVUVUVEUgUExPVApgYGB7cn0KZnZpel9zaWxob3VldHRlKHBhbV9maXQpCmBgYAoKIyMjIENMQVJBCiMjIyMgQ0xVU1RFUiBQTE9UCmBgYHtyfQpsaWJyYXJ5KGNsdXN0ZXIpCmNsYXJhQzIgPSBjbGFyYShudW1lcmljVmFyaWFibGVzLCAyLAogICAgICAgICAgICAgICAgICAgIHNhbXBsZXMgPSAxMDAwLCBjb3JyZWN0LmQgPSBGQUxTRSkKZnZpel9jbHVzdGVyKGNsYXJhQzIsIGdlb20gPSAicG9pbnQiLCBwb2ludHNpemUgPSAxLCBlbGxpcHNlLnR5cGUgPSAibm9ybSIpCmBgYAoKCgojIyMjIENPTlRJTkdFTkNZIFRBQkxFCmBgYHtyfQpjbGFyYUMyJGNsdXN0ZXJpbmcgPC0gYXMuZmFjdG9yKGNsYXJhQzIkY2x1c3RlcmluZykKdGFibCA9IHByb3AudGFibGUodGFibGUoY2xhcmFDMiRjbHVzdGVyaW5nLGlzQ2FuY2VsZWRfc2FtcGxlKSkqMTAwCnRhYmwKYGBgCgpgYGB7cn0KdGFibFsxLDJdLyh0YWJsWzFdICsgdGFibFsxLDJdKSoxMDAgIyAlIG9mIENhbmNlbGF0aW9ucyBvZiBHcm91cCAxCnRhYmxbMiwyXS8odGFibFsyXSArIHRhYmxbMiwyXSkqMTAwICMgJSBvZiBDYW5jZWxhdGlvbnMgb2YgR3JvdXAgMgpgYGAKIyMjIyBTSUxIT1VFVFRFIFBMT1QKYGBge3J9CmZ2aXpfc2lsaG91ZXR0ZShjbGFyYUMyKQpgYGAKCiMjIyBGVVpaWSBBTkFMWVNJUyBDTFVTVEVSSU5HCiMjIyMgQ0xVU1RFUiBQTE9UCmBgYHtyfQpsaWJyYXJ5KFJ0c25lKQpmYW5ueV9maXQgPC0gZmFubnkoZ293ZXJfZGlzdCwgZGlzcyA9IFRSVUUsIGsgPSAyKQoKdHNuZV9vYmogPC0gUnRzbmUoZ293ZXJfZGlzdCwgaXNfZGlzdGFuY2UgPSBUUlVFKQp0c25lX2RhdGEgPC0gdHNuZV9vYmokWSAlPiUKICBkYXRhLmZyYW1lKCkgJT4lCiAgc2V0TmFtZXMoYygiWCIsICJZIikpICU+JQogIG11dGF0ZShjbHVzdGVyID0gZmFjdG9yKGZhbm55X2ZpdCRjbHVzdGVyaW5nKSkKZ2dwbG90KGFlcyh4ID0gWCwgeSA9IFkpLCBkYXRhID0gdHNuZV9kYXRhKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjbHVzdGVyKSkKYGBgCmBgYHtyfQojIENvZWZpY2llbnRlIGRlIHNlZ21lbnRhY2nDs24gbm9ybWFsaXphZG86CmZhbm55X2ZpdCRjb2VmZgpgYGAKIyMjIyBDT05USU5HRU5DWSBUQUJMRQpgYGB7cn0KZmFubnlfZml0JGNsdXN0ZXJpbmcgPC0gYXMuZmFjdG9yKGZhbm55X2ZpdCRjbHVzdGVyaW5nKQp0YWJsID0gcHJvcC50YWJsZSh0YWJsZShmYW5ueV9maXQkY2x1c3RlcmluZyxpc0NhbmNlbGVkX3NhbXBsZSkpKjEwMAp0YWJsCmBgYApgYGB7cn0KdGFibFsxLDJdLyh0YWJsWzFdKyB0YWJsWzEsMl0pKjEwMCAjICUgb2YgQ2FuY2VsYXRpb25zIG9mIEdyb3VwIDEKdGFibFsyLDJdLyh0YWJsWzJdK3RhYmxbMiwyXSkqMTAwICMgJSBvZiBDYW5jZWxhdGlvbnMgb2YgR3JvdXAgMgpgYGAKCiMjIyMgU0lMSE9VRVRURSBQTE9UCmBgYHtyfQpmdml6X3NpbGhvdWV0dGUoZmFubnlfZml0KQpgYGAKIyBESU1FTlNJT04gUkVEVUNUSU9OCgojIyBDT1JSRVNQT05ERU5DRSBBTkFMWVNJUwojIyMgUkVTRVJWRUQgUk9PTSBWUyAgRElTVFJJQlVUSU9OIENIQU5ORUwKYGBge3J9CnJvb21DaGFubmVsID0gYXMuZGF0YS5mcmFtZS5tYXRyaXgodGFibGUocmF3X2RhdGEkRGlzdHJpYnV0aW9uQ2hhbm5lbCwgcmF3X2RhdGEkUmVzZXJ2ZWRSb29tVHlwZSkpCnJvb21DaGFubmVsCgphcy50YWJsZShhcy5tYXRyaXgocm9vbUNoYW5uZWwpKQpgYGAKYGBge3J9CnJvb21DaGFubmVsU2NhbGVkID0gc2NhbGUocm9vbUNoYW5uZWwpCnJvb21DaGFubmVsU2NhbGVkCmBgYAoKIyMjIyBJbmRlcGVuZGVuY2UgVGVzdApgYGB7cn0KdGVzdC5pbmQgPSBjaGlzcS50ZXN0KHJvb21DaGFubmVsKQp0ZXN0LmluZAoKIyBUaGUgcmVzdWx0IG9mIHRoZSB0ZXN0IGNvbmZpcm1zIHRoZSBwb3NzaWJpbGl0eSBvZiByZWplY3RpbmcgdGhlIGh5cG90aGVzZXMgb2YgaW5kZXBlbmRlbmNlIGJldHdlZW4gY2F0ZWdvcmllcyBvZiByb3dzIGFuZCBjb2x1bW5zLCBvciwgd2hhdCBpcyB0aGUgc2FtZSwgd2UgY2FuIGFmZmlybSB0aGF0IHRoZXJlIGlzIGEgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlbS4gVGhlIHJlc3Qgb2YgdGhlIHRlc3RzIHByZXNlbnRlZCBiZWxvdyBhbGxvdyB1cyB0byBoZWxwIHZpc3VhbGl6ZSBhbmQgaW50ZXJwcmV0IHRoZSB0ZXN0IHJlc3VsdC4KYGBgCgoKCiMjIyMgQW5hbHlzaXMKYGBge3J9CnJvb21DaGFubmVsdCA9IGFzLnRhYmxlKGFzLm1hdHJpeChyb29tQ2hhbm5lbCkpCnJvb21DaGFubmVsdApgYGAKYGBge3J9CmxpYnJhcnkoIkZhY3RvTWluZVIiKQojIHRhYjEgPC0gYXMuZGF0YS5mcmFtZS5tYXRyaXgodGFibGUoYXMuZmFjdG9yKGRmJFgxKSxhcy5mYWN0b3IoZGYkWDIpKSkKIyByZXMuY2EgPC0gQ0EodGFiMSwgZ3JhcGggPSBGQUxTRSkKYGBgCgpgYGB7cn0KbGlicmFyeShGYWN0b01pbmVSKQpyb29tQ2hhbm5lbC5jYT1DQShyb29tQ2hhbm5lbCwgZ3JhcGggPSBGQUxTRSkKYGBgCgpgYGB7cn0Kc3VtbWFyeShyb29tQ2hhbm5lbC5jYSkKYGBgCgpgYGB7cn0KZnZpel9jYV9iaXBsb3Qocm9vbUNoYW5uZWwuY2EsIG1hcCA9InJvd3ByaW5jaXBhbCIsIGFycm93ID0gYyhUUlVFLCBUUlVFKSkKYGBgCgpgYGB7cn0KZnZpel9jYV9iaXBsb3Qocm9vbUNoYW5uZWwuY2EsIG1hcCA9ImNvbGdyZWVuIiwKICAgICAgICAgICAgICAgYXJyb3cgPSBjKFRSVUUsIEZBTFNFKSkrCiAgICAgICAgZ2d0aXRsZSgiQ29udHJpYnV0aW9uIG9mIERpc3RyaWJ1dGlvbiBDaGFubmVscyB0byB0aGUgZGltZW5zaW9ucyIpCmBgYAoKYGBge3J9CmZ2aXpfY2FfYmlwbG90KHJvb21DaGFubmVsLmNhLCBtYXAgPSJyb3dncmVlbiIsCiAgICAgICAgICAgICAgIGFycm93ID0gYyhGQUxTRSwgVFJVRSkpKwogICAgICAgIGdndGl0bGUoIkNvbnRyaWJ1dGlvbiBvZiBSb29tIFR5cGVzIHRvIHRoZSBkaW1lbnNpb25zIikKYGBgCgoKCiMjIyBDT1VOVFJZICBWUyAgUkVTRVJWRUQgUk9PTQpgYGB7cn0KY291bnRyeVJvb20gPSBhcy5kYXRhLmZyYW1lLm1hdHJpeCh0YWJsZShyYXdfZGF0YSRDb3VudHJ5LCByYXdfZGF0YSRSZXNlcnZlZFJvb21UeXBlKSkKY291bnRyeVJvb20KYGBgCiMjIyMgSW5kZXBlbmRlbmNlIFRlc3QKYGBge3J9CnRlc3QuaW5kID0gY2hpc3EudGVzdChjb3VudHJ5Um9vbSkKdGVzdC5pbmQKCiMgVGhlIHJlc3VsdCBvZiB0aGUgdGVzdCBjb25maXJtcyB0aGUgcG9zc2liaWxpdHkgb2YgcmVqZWN0aW5nIHRoZSBoeXBvdGhlc2VzIG9mIGluZGVwZW5kZW5jZSBiZXR3ZWVuIGNhdGVnb3JpZXMgb2Ygcm93cyBhbmQgY29sdW1ucywgb3IsIHdoYXQgaXMgdGhlIHNhbWUsIHdlIGNhbiBhZmZpcm0gdGhhdCB0aGVyZSBpcyBhIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZW0uIFRoZSByZXN0IG9mIHRoZSB0ZXN0cyBwcmVzZW50ZWQgYmVsb3cgYWxsb3cgdXMgdG8gaGVscCB2aXN1YWxpemUgYW5kIGludGVycHJldCB0aGUgdGVzdCByZXN1bHQuCmBgYAoKCgoKCiMjIyMgQW5hbHlzaXMKCmBgYHtyfQpsaWJyYXJ5KEZhY3RvTWluZVIpCmNvdW50cnlSb29tLmNhPUNBKGNvdW50cnlSb29tLCBncmFwaCA9IEZBTFNFKQpgYGAKCgpgYGB7cn0Kc3VtbWFyeShjb3VudHJ5Um9vbS5jYSkKYGBgCmBgYHtyfQpmdml6X2NhX2JpcGxvdChjb3VudHJ5Um9vbS5jYSwgbWFwID0icm93cHJpbmNpcGFsIiwgYXJyb3cgPSBjKFRSVUUsIFRSVUUpKQpgYGAKYGBge3J9CmZ2aXpfY2FfYmlwbG90KGNvdW50cnlSb29tLmNhLCBtYXAgPSJjb2xncmVlbiIsCiAgICAgICAgICAgICAgIGFycm93ID0gYyhUUlVFLCBGQUxTRSkpKwogICAgICAgIGdndGl0bGUoIkNvbnRyaWJ1dGlvbiBvZiBDb3VudHJ5IHRvIHRoZSBkaW1lbnNpb25zIikKYGBgCmBgYHtyfQpmdml6X2NhX2JpcGxvdChjb3VudHJ5Um9vbS5jYSwgbWFwID0icm93Z3JlZW4iLAogICAgICAgICAgICAgICBhcnJvdyA9IGMoRkFMU0UsIFRSVUUpKSsKICAgICAgICBnZ3RpdGxlKCJDb250cmlidXRpb24gb2YgUm9vbSBUeXBlIHRvIHRoZSBkaW1lbnNpb25zIikKYGBgCgoKIyMgUENBCmBgYHtyfQojIE9ubHkgbnVtZXJpYyBWYXJpYWJsZXMgZm9yIGNsdXN0ZXJpbmcKbnVtZXJpY1ZhcmlhYmxlcyA8LSBwcmVkaWN0aXZlVmFyaWFibGVzTm9uWmVyb1tjKDE3LDcsMiwzLDksMTMsOCwxNiwxLDEyLDExLDE4LDUsNiwxOSldCm5vcm1hbGl6ZURhdGEgPC0gZnVuY3Rpb24oeCl7CiAgbSA8LSBtZWFuKHgpCiAgcyA8LSBzZCh4KQogIHggPC0gKHggLSBtKS9zCn0KCm51bWVyaWNWYXJpYWJsZXMgPC0gYXBwbHkobnVtZXJpY1ZhcmlhYmxlcywgMiwgbm9ybWFsaXplRGF0YSkKYGBgCiMjIyBFWFBMT1JBVE9SWSBGQUNUT1IgQU5BTFlTSVMKSW4gb3JkZXIgdG8gYXBwbHkgdGhlc2UgdW5zdXBlcnZpc2VkIGxlYXJuaW5nIHRlY2huaXF1ZXMsIGkuZS4gUENBLCBvdXIgZGF0YSBzZXQgbXVzdCBoYXZlIHNvbWUgcHJvcGVydGllczoKCiMjIyMgRGV0ZXJtaW5hbnQgb2YgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeAoKVGhlIGRldGVybWluYW50IG9mIG91ciBjb3JyZWxhdGlvbiBtYXRyaXggaXMgMC4wNDcyMDQ4OC4gQXMgd2UgaGF2ZSBhIGxvdyBkZXRlcm1pbmFudCBuZWFyIHRvIDAsIGl0IG1ha2VzIHNlbnNlIHRvIGFwcGx5IFBDQSB0byByZWR1Y2UgZGltZW5zaW9ucywgYmVjYXVzZSBpdCBhbHNvIHRlbGxzIHVzIHRoYXQgdGhlcmUgaXMgYSBoaWdoIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHZhcmlhYmxlcyBzdHVkaWVkLgoKYGBge3IgIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTIuNX0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBERVRFUk1JTkFOVCBPRiBUSEUgQ09SUkVMQVRJT04gTUFUUklYIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmRldChjb3IobnVtZXJpY1ZhcmlhYmxlcywgCiAgICAgICAgdXNlID0gImNvbXBsZXRlLm9icyIpKQpgYGAKIyMjIyBLTU8KCkZvciByZWZlcmVuY2UsIEthaXNlciBwdXQgdGhlIGZvbGxvd2luZyB2YWx1ZXMgb24gdGhlIHJlc3VsdHM6CjAuMDAgdG8gMC40OSB1bmFjY2VwdGFibGUuCjAuNTAgdG8gMC41OSBtaXNlcmFibGUuCjAuNjAgdG8gMC42OSBtZWRpb2NyZS4KMC43MCB0byAwLjc5IG1pZGRsaW5nLgowLjgwIHRvIDAuODkgbWVyaXRvcmlvdXMuCjAuOTAgdG8gMS4wMCBtYXJ2ZWxvdXMuCgpTbyBpbiBvdXIgY2FzZSwgb3VyIHZhcmlhYmxlcyBhcmUgbWVyaXRvcmlvdXMgdG8gYXBwbHkgZmFjdG9yIGFuYWx5c2lzIGFzIG91ciBPdmVyYWxsIE1TQSA9IDAuOC4KCmBgYHtyICBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD0yLjV9CmxpYnJhcnkocHN5Y2gpCiMgTWVhc3VyZSBvZiBTYW1wbGUgQWRlcXVhY3kgdGhyb3VnaCB0aGUgZmFjdG9yIG9mIEthaXNlci1NZXllci1PbGtpbiBmYWN0b3IgYWRlcXVhY3kKS01PKGNvcihudW1lcmljVmFyaWFibGVzLCAKICAgICAgICB1c2UgPSAiY29tcGxldGUub2JzIikpCmBgYAojIyMjIEJhcnRsZXR0J3MgVGVzdCBvZiBTcGhlcmljaXR5CgpJbiBvdXIgY2FzZSB0aGUgcC12YWx1ZSBpcyBlcXVhbCB0byAwLCBzbyB0aGUgZmFjdG9yIGFuYWx5c2lzIHdpbGwgYmUgdXNlZnVsIHdpdGggb3VyIGRhdGEgYXMgd2UgYXJlIGFjY2VwdGluZyB0aGUgYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyB0aGF0IG91ciB2YXJpYWJsZXMgYXJlIGNvcnJlbGF0ZWQgZW5vdWdoIGFuZCBkaXZlcmdpbmcgZnJvbSB0aGUgaWRlbnRpdHkgbWF0cml4LgoKYGBge3IgIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTIuNX0KcF92YWx1ZSA8LSBjb3J0ZXN0LmJhcnRsZXR0KGNvcihudW1lcmljVmFyaWFibGVzKSwgbiA9IE5VTEwsZGlhZz1UUlVFKSRwLnZhbHVlCnBfdmFsdWUKYGBgCgojIyMjIENvbmNsdXNpb25zIG9mIHRoZSBkYXRhIGV4cGxvcmF0b3J5IGFuYWx5c2lzCgpBZnRlciBkb2luZyBhbGwgdGhlc2UgdGVzdHMgd2UgY2FuIGFwcGx5IGEgZGF0YSByZWR1Y3Rpb24gdGVjaG5pcXVlIHN1Y2ggYXMgdGhlIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMKd2l0aCBjb25maWRlbmNlLgoKCgojIyMgUENBCgpBIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiB0ZWNobmlxdWUgc3VjaCBhcyBhIFBDQSBtYWtlcyBzZW5zZSB0byBzZWUgd2hhdCB2YXJpYWJsZXMgY2FuIGJlIGV4cGxhaW5lZCBpbiB0aGUgc2FtZSBkaW1lbnNpb24uCgpgYGB7ciAgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgZmlnLndpZHRoPTMsIGZpZy5oZWlnaHQ9Mi41fQpsaWJyYXJ5KEZhY3RvTWluZVIpCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBJbmRpdmlkdWFscyBmYWN0b3IgbWFwIGFuZCBWYXJpYWJsZXMgZmFjdG9yIG1hcChQQ0EpIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnBjYSA9IFBDQShudW1lcmljVmFyaWFibGVzLCBncmFwaCA9IFQsc2NhbGUudW5pdCA9IEZBTFNFKSMgc2NhbGVzIGJ5IGRlZmF1bHQKYGBgCgpBcyB3ZSBjYW4gc2VlIGluIEluZGl2aWR1YWxzIGZhY3RvciBtYXAsIHRoZSBkaXJlY3Rpb25zIG9mIERpbTEgYW5kIERpbTIgYXJlIGNsZWFybHkgb3J0aG9nb25hbCBhbmQgZXhwbGFpbiAxMC40JSBvZiB0aGUgdmFyaWFuY2U6CgotIDYlIG9mIHRoZSB2YXJpYW5jZSBjYW4gYmUgZXhwbGFpbmVkIGluIERpbTEuCgotIDQuNCUgb2YgdGhlIHZhcmlhbmNlIGNhbiBiZSBleHBsYWluZWQgaW4gRGltMi4KCklmIHRoZSBmaXJzdCB0d28gZmFjdG9ycyB0b2dldGhlciBleHBsYWluIG1vc3Qgb2YgdGhlIHZhcmlhYmlsaXR5IGluIHRoZSBvcmlnaW5hbCA5IHZhcmlhYmxlcywgdGhlbiB0aG9zZSBmYWN0b3JzIGFyZSBjbGVhcmx5IGEgZ29vZCwgc2ltcGxlciBzdWJzdGl0dXRlIGZvciBhbGwgOSB2YXJpYWJsZXMuIFdlIGNhbiBkcm9wIHRoZSByZXN0IHdpdGhvdXQgbG9zaW5nIG11Y2ggb2YgdGhlIG9yaWdpbmFsIHZhcmlhYmlsaXR5LgoKQnV0IGlmIGl0IHRha2VzIDcgZmFjdG9ycyB0byBleHBsYWluIG1vc3Qgb2YgdGhlIHZhcmlhbmNlIGluIHRob3NlIDkgdmFyaWFibGVzLCB3ZSBtaWdodCBhcyB3ZWxsIGp1c3QgdXNlIHRoZSBvcmlnaW5hbCA5LgoKSW4gdGhpcyBjYXNlIHdlIHByb2JhYmx5IG5lZWQgbW9yZSB0aGFuIDIgZGltZW5zaW9ucyB0byBleHBsYWluIG1vc3Qgb2YgdGhlIHZhcmlhbmNlIGFzIHdlIGFyZSBvbmx5IGV4cGxhaW5pbmcgNTMlIG9mIHRoZSB2YXJpYW5jZS4KCgoKIyMjIyBMb2FkaW5ncy9Db29yZGluYXRlcwoKYGBge3IgIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aD0zLCBmaWcuaGVpZ2h0PTMuNX0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTG9hZGluZ3MvQ29vcmRpbmF0ZXMjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgVGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gYSB2YXJpYWJsZSBhbmQgYSBQQyBpcyBjYWxsZWQgbG9hZGluZy4gVGhlIHZhcmlhYmxlcyBjYW4gYmUgcGxvdHRlZCBhcyBwb2ludHMgaW4gdGhlIGNvbXBvbmVudAojIHNwYWNlIHVzaW5nIHRoZWlyIGxvYWRpbmdzIGFzIGNvb3JkaW5hdGVzLiBIZXJlIHdlIGNhbiBzZWUgdGhlIGNvb3JkaW5hdGVzIHRoYXQgY29ycmVzcG9uZCB3aXRoIHRoZSBWYXJpYWJsZXMgZmFjdG9yIG1hcCAoUENBKS4KcGNhJHZhciRjb29yZApgYGAKSGVyZSB3ZSBzdGFydCBzZWVpbmcgdGhhdCB3aXRoIG1vcmUgZGltZW5zaW9ucyB3ZSBhcmUgYWJsZSB0byBleHBsYWluIGEgaGlnaGVyIHBlcmNlbnRhZ2Ugb2Ygb3VyIGRhdGFzZXQgYnV0IHdlIHN0aWxsIGRvbid0IHNlZSBhIGNsZWFyIGRpZmZlcmVuY2UgYmV0d2VlbiB3aGF0IHRoZSBkaWZmZXJlbnQgZGltZW5zaW9ucyBleHBsYWluLgoKCiMjIyMgY29zMjogcXVhbGl0eSBvZiB0aGUgcmVwcmVzZW50YXRpb24gZm9yIHZhcmlhYmxlcyBvbiB0aGUgZmFjdG9yIG1hcAoKYGBge3IgIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTIuNX0KcGNhJHZhciRjb3MyCiMgbGF5b3V0LnNob3cobGF5b3V0KG1hdHJpeChjKDEpLG5jb2w9MSkpKQoKYGBgCgoKCiMjIyMgU2NyZWUgUGxvdAoKYGBge3IgIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTQuNX0KIyMjIyMjIyMjIyMjIwojIFNjcmVlIFBsb3QjCiMjIyMjIyMjIyMjIyMKIyBUaGUgcG9pbnQgd2hlcmUgdGhlIHNsb3BlIG9mIHRoZSBjdXJ2ZSBpcyBjbGVhcmx5IGxldmVsaW5nIG9mZiAodGhlIOKAnGVsYm93KSBpbmRpY2F0ZXMgdGhlIG51bWJlciBvZiBmYWN0b3JzCiMgdGhhdCBzaG91bGQgYmUgZ2VuZXJhdGVkIGJ5IHRoZSBhbmFseXNpcy4KZnZpel9laWcocGNhLCBhZGRsYWJlbHMgPSBUUlVFLCBoanVzdCA9IC0wLjMpICsgCiAgbGFicyh0aXRsZSA9ICJTY3JlZSBwbG90IiwgeCA9ICJEaW1lbnNpb25zIiwgeSA9ICIlIFZhcmlhbmNlIGV4cGxhaW5lZCIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgojIyMjIENvbnRyaWJ1dGlvbnMKCkFsbCB2YXJpYWJsZXMgY29udHJpYnV0ZSB0byBEaW0xIG9yIERpbTIgYWJvdmUgdGhlIHRocmVzaG9sZC4KSWYgdGhlIGNvbnRyaWJ1dGlvbiBvZiB0aGUgdmFyaWFibGVzIHdlcmUgdW5pZm9ybSwgdGhlIGV4cGVjdGVkIHZhbHVlIHdvdWxkIGJlIDEvbGVuZ3RoKHZhcmlhYmxlcykgCgpTbyB0aGlzIGlzIHRoZSBleHBlY3RlZCBhdmVyYWdlIGNvbnRyaWJ1dGlvbiB0aGF0IHdlIGNhbiB1c2UgYXMgYSB0aHJlc2hvbGQuCgpgYGB7ciAgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NC41fQpmdml6X2NvbnRyaWIocGNhLCBjaG9pY2U9InZhciIsIGF4ZXMgPSAxICkrCiAgbGFicyh0aXRsZSA9ICJDb250cmlidXRpb25zIHRvIERpbSAxIikKIyBJZiB0aGUgY29udHJpYnV0aW9uIG9mIHRoZSB2YXJpYWJsZXMgd2VyZSB1bmlmb3JtLCB0aGUgZXhwZWN0ZWQgdmFsdWUgd291bGQgYmUgMS9sZW5ndGgodmFyaWFibGVzKSA9IDEvOSA9IDExLjElLiBTbyB0aGlzIGlzCiMgdGhlIGV4cGVjdGVkIGF2ZXJhZ2UgY29udHJpYnV0aW9uIHRoYXQgd2UgY2FuIHVzZSBhcyBhIHRocmVzaG9sZC4KYGBgCgpVc2luZyB0aGF0IGFzIGEgdGhyZXNob2xkLCBoZXJlIHdlIGNhbiBzZWUgdGhlIGhpZ2hlc3QgY29udHJpYnV0aW9ucyB0byBEaW0xOiAKCmBgYHtyICBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LjV9CmZ2aXpfY29udHJpYihwY2EsIGNob2ljZT0idmFyIiwgYXhlcyA9IDIgKSsKICBsYWJzKHRpdGxlID0gIkNvbnRyaWJ1dGlvbnMgdG8gRGltIDIiKQpgYGAKClVzaW5nIHRoYXQgYXMgYSB0aHJlc2hvbGQsIGhlcmUgd2UgY2FuIHNlZSB0aGUgaGlnaGVzdCBjb250cmlidXRpb25zIHRvIERpbTI6IGxlbmd0aCwgbGJoLCB3c2t1bGwsIGx0aWJpby4KCgoKIyMjIyBFaWdlbnZhbHVlcwpgYGB7ciAgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NC41fQpnZXRfZWlnKHBjYSkKYGBgCgpUaGUgdmFyaWFuY2UgcGVyY2VudGFnZSBhbmQgY3VtdWxhdGl2ZSB2YXJpYW5jZSBwZXJjZW50YWdlIGNoYW5nZXMgbGVzcyBvbmNlIHdlIGdldCB0byB0aGUgZm91cnRoIG9yIGZpZnRoIGRpbWVuc2lvbi4KCgoKYGBge3IgIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTQuNX0KIyMjIyMjIyMjIyMjIyMjIyMjIwojIFZhcmltYXggbWV0aG9kIDIjCiMjIyMjIyMjIyMjIyMjIyMjIyMKIyBOb3cgd2l0aCB2YXJpbWF4IHJvdGF0aW9uLCBLYWlzZXItbm9ybWFsaXplZCAKIyBieSBkZWZhdWx0OgpwYzIgPSBwc3ljaDo6cHJpbmNpcGFsKGNvcihudW1lcmljVmFyaWFibGVzKSwgbmZhY3RvcnM9MiwgCiAgICAgICAgICAgICAgICAgICAgICAgcm90YXRlID0gInZhcmltYXgiLCBzY29yZXMgPSBUUlVFKQpwYzIKCmBgYAoKYGBge3IgIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTQuNX0KcGMyJGxvYWRpbmdzCnBjYSR2YXIkY29vcmQKYGBgCgpFbCBzZWd1bmRvIGRvY3VtZW50bywgZGUgZXh0ZW5zacOzbiBtw6F4aW1hIDYgcHAsIHJlc29sdmVyw6EgdW4gcHJvYmxlbWEgZGUgcHJlZGljY2nDs24gc2llbmRvIGxhIHZhcmlhYmxlIG9iamV0aXZvIHByZWRlY2lyIHNpIGxhIHJlc2VydmEgc2UgY2FuY2VsYSBvIG5vLCB1dGlsaXphbmRvIGxhcyB0w6ljbmljYXMgZGUgcmVncmVzacOzbiBlc3R1ZGlhZGFzIGVuIGxhIGFzaWduYXR1cmEuCgoKI1BSRURJQ1RJT04KCiMjIExPR0lTVElDIFJFR1JFU1NJT04KTG9naXN0aWMgcmVncmVzc2lvbiBpcyBhIG1ldGhvZCBmb3IgZml0dGluZyBhIHJlZ3Jlc3Npb24gY3VydmUsIHkgPSBmKHgpLCB3aGVuIHkgaXMgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZS4KVGhlIHR5cGljYWwgdXNlIG9mIHRoaXMgbW9kZWwgaXMgcHJlZGljdGluZyB5IGdpdmVuIGEgc2V0IG9mIHByZWRpY3RvcnMgeC4gVGhlIHByZWRpY3RvcnMgY2FuIGJlIGNvbnRpbnVvdXMsIGNhdGVnb3JpY2FsIG9yIGEgbWl4IG9mIGJvdGguCi0gUFJFRElDVElWRSBNT0RFTDogTXVsdGljb2xpbmVhbGl0eSBpcyBub3QgcmVsZXZhbnQgYXMgd2UgYXJlIG5vdCBzZWFyY2hpbmcgZm9yIGFuIGV4cGxhbmF0b3J5IG1vZGVsLgojIyMgVFJBSU4gQU5EIFRFU1QKYGBge3J9CiNUUkFJTklORyBBTkQgVEVTVElORyBEQVRBCnNldC5zZWVkKDEzMTgyMikKbiA8LSBucm93KHJhd19kYXRhX3ByZWRpY3Rpb24pCnNjYWxlREYgIDwtIHNjYWxlKHJhd19kYXRhX3ByZWRpY3Rpb24pCmlkX3RyYWluIDwtIHNhbXBsZSgxOm4gLCAwLjgwKm4pCmRmLnRyYWluIDwtIGFzLmRhdGEuZnJhbWUoc2NhbGVERltpZF90cmFpbixdKQpkZi50cmFpbiRJc0NhbmNlbGVkIDwtIHJhd19kYXRhW2lkX3RyYWluLF0kSXNDYW5jZWxlZApkZi50ZXN0ICA8LSBhcy5kYXRhLmZyYW1lKHNjYWxlREZbLWlkX3RyYWluLF0pCmRmLnRlc3QkSXNDYW5jZWxlZCA8LSByYXdfZGF0YVstaWRfdHJhaW4sXSRJc0NhbmNlbGVkCmBgYAojIyMjIFRyYWluIFRhcmdldCBWYXJpYWJsZSAlCmBgYHtyfQp0YXJnZXQgPC0gYXMuZGF0YS5mcmFtZShkZi50cmFpbiAlPiUgZ3JvdXBfYnkoSXNDYW5jZWxlZCkgJT4lIHN1bW1hcmlzZShjb3VudHMgPSBuKCkpKSAlPiUgbXV0YXRlKHBlcmMgPSBjb3VudHMvbnJvdyhkZi50cmFpbikpCmdncGxvdCh0YXJnZXQsIGFlcyh4ID0gSXNDYW5jZWxlZCwgeSA9IHBlcmMpKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKHBlcmMsMikpKQpgYGAKIyMjIyBUZXN0IFRhcmdldCBWYXJpYWJsZSAlCmBgYHtyfQp0YXJnZXQgPC0gYXMuZGF0YS5mcmFtZShkZi50ZXN0ICU+JSBncm91cF9ieShJc0NhbmNlbGVkKSAlPiUgc3VtbWFyaXNlKGNvdW50cyA9IG4oKSkpICU+JSBtdXRhdGUocGVyYyA9IGNvdW50cy9ucm93KGRmLnRlc3QpKQpnZ3Bsb3QodGFyZ2V0LCBhZXMoeCA9IElzQ2FuY2VsZWQsIHkgPSBwZXJjKSkgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZChwZXJjLDIpKSkKYGBgCiMjIyBMb2dpc3RpYyBSZWdyZXNzaW9uCmBgYHtyfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENPU1QgSU4gVFJBSU5JTkcgU0VUIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzZWFyY2hncmlkID0gc2VxKDAuMDAwMSwwLjYsIDAuMDEpCnJlc3VsdCA9IGNiaW5kKHNlYXJjaGdyaWQsIE5BKQojIGluIHRoZSBjb3N0IGZ1bmN0aW9uLCBib3RoIHIgYW5kIHBpIGFyZSB2ZWN0b3JzLCByPXRydXRoLCBwaT1wcmVkaWN0ZWQgcHJvYmFiaWxpdHkKY29zdDEgPC0gZnVuY3Rpb24ociwgcGkpewogIHdlaWdodDEgPC0gMQogIHdlaWdodDAgPC0gMQogIGMxIDwtIChyID09IDEpICYgKHBpIDwgcGN1dCkgIyB0cnVlIGlmIGFjdHVhbCAxIGJ1dCBwcmVkaWN0IDAuIEZQIChGYWxzZSBQb3NpdGl2ZSkKICBjMCA8LSAociA9PSAwKSAmIChwaSA+IHBjdXQpICN0cnVlIGlmIGFjdHVhbCAwIGJ1dCBwcmVkaWN0IDEuIEZOIChGYWxzZSBOZWdhdGl2ZSkKICByZXR1cm4obWVhbih3ZWlnaHQxICogYzEgKyB3ZWlnaHQwICogYzApKQp9CmBgYAojIyMjIFRyYWluIGFuZCBUZXN0IHNldHMgZm9yIFJlZ3VsYXJpemF0aW9uCmBgYHtyfQojIyBUcmFpbgpkYXRvc190cmFpbl94IDwtIG1vZGVsLm1hdHJpeChJc0NhbmNlbGVkIH4gLiwgZGYudHJhaW4pWywgLTFdCmRhdG9zX3RyYWluX3kgPC0gZGYudHJhaW4kSXNDYW5jZWxlZAojIyB0ZXN0CmRhdG9zX3Rlc3RfeCA8LSBtb2RlbC5tYXRyaXgoSXNDYW5jZWxlZCB+IC4sIGRmLnRlc3QpWywgLTFdCmRhdG9zX3Rlc3RfeSA8LSBkZi50ZXN0JElzQ2FuY2VsZWQKYGBgCiMjIyMgTG9naXN0aWMgUmVncmVzc2lvbiBtb2RlbApgYGB7cn0KZGYuZ2xtMSA8LSBnbG0oSXNDYW5jZWxlZCB+IC4sIGZhbWlseSA9IGJpbm9taWFsLCBkZi50cmFpbikKcHJvYiA8LSBwcmVkaWN0KGRmLmdsbTEsdHlwZSA9ICJyZXNwb25zZSIpCmZvciAoaSBpbiAxOmxlbmd0aChzZWFyY2hncmlkKSkKewogIHBjdXQgPC0gcmVzdWx0W2ksMV0KICAjYXNzaWduIHRoZSBjb3N0IHRvIHRoZSAybmQgY29sCiAgcmVzdWx0W2ksMl0gPC0gY29zdDEoZGYudHJhaW4kSXNDYW5jZWxlZCwgcHJvYikKfQpwbG90KHJlc3VsdCwgeWxhYiA9ICJDb3N0IGluIFRyYWluaW5nIFNldCIpCnJlc3VsdFt3aGljaC5taW4ocmVzdWx0WywyXSksXQojIHNlYXJjaGdyaWQgICAgICAgICAgICAKIyAwLjQ0MDEwMDAgIDAuMTUxMjczMQpgYGAKIyMjIFBSRURJQ1RJT04gVFJBSU4KYGBge3J9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmN1dF9vZmYgPC0gMC40NDAxMDAwIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpwcm9iLmdsbTEuaW5zYW1wbGUgPC0gcHJlZGljdChkZi5nbG0xLHR5cGUgPSAicmVzcG9uc2UiKQpwcmVkaWN0ZWQuZ2xtMS5pbnNhbXBsZSA8LSAocHJvYi5nbG0xLmluc2FtcGxlID4gY3V0X29mZikKcHJlZGljdGVkLmdsbTEuaW5zYW1wbGUgPC0gYXMubnVtZXJpYyhwcmVkaWN0ZWQuZ2xtMS5pbnNhbXBsZSkKIyMjIyMjIyMjIyMjIyMjIyMjIwojIENPTkZVU0lPTiBNQVRSSVgjCiMjIyMjIyMjIyMjIyMjIyMjIyMKdGFibGUoZGYudHJhaW4kSXNDYW5jZWxlZCwgcHJlZGljdGVkLmdsbTEuaW5zYW1wbGUsIGRubiA9IGMoIlRydXRoIiwiUHJlZGljdGVkIikpCiMjIyMjIyMjCiMgRVJST1IjCiMjIyMjIyMjCm1lYW4oaWZlbHNlKGRmLnRyYWluJElzQ2FuY2VsZWQgIT0gcHJlZGljdGVkLmdsbTEuaW5zYW1wbGUsIDEsIDApKQoKbWVhbihpZmVsc2UoZGYudHJhaW4kSXNDYW5jZWxlZCA9PSBwcmVkaWN0ZWQuZ2xtMS5pbnNhbXBsZSwgMSwgMCkpCmBgYAojIyMgUFJFRElDVElPTiBURVNUCmBgYHtyfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpjdXRfb2ZmIDwtIDAuNDQwMTAwMCMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKcHJvYi5nbG0xLm91dHNhbXBsZSA8LSBwcmVkaWN0KGRmLmdsbTEsIHR5cGUgPSAicmVzcG9uc2UiLG5ld2RhdGEgPSBkZi50ZXN0KQpwcmVkaWN0ZWQuZ2xtMS5vdXRzYW1wbGUgPC0gKHByb2IuZ2xtMS5vdXRzYW1wbGUgPiBjdXRfb2ZmKQpwcmVkaWN0ZWQuZ2xtMS5vdXRzYW1wbGUgPC0gYXMubnVtZXJpYyhwcmVkaWN0ZWQuZ2xtMS5vdXRzYW1wbGUpCiMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDT05GVVNJT04gTUFUUklYIwojIyMjIyMjIyMjIyMjIyMjIyMjCnRhYmxlKGRmLnRlc3QkSXNDYW5jZWxlZCwgcHJlZGljdGVkLmdsbTEub3V0c2FtcGxlLCBkbm4gPSBjKCJUcnV0aCIsIlByZWRpY3RlZCIpKQojIyMjIyMjIwojIEVSUk9SIwojIyMjIyMjIwptZWFuKGlmZWxzZShkZi50ZXN0JElzQ2FuY2VsZWQgIT0gcHJlZGljdGVkLmdsbTEub3V0c2FtcGxlLCAxLCAwKSkKCm1lYW4oaWZlbHNlKGRmLnRyYWluJElzQ2FuY2VsZWQgPT0gcHJlZGljdGVkLmdsbTEuaW5zYW1wbGUsIDEsIDApKQpgYGAKCgojIyMgUk9DIENVUlZFCk91ciBBcmVhIFVuZGVyIHRoZSBDdXJ2ZSBpcyAKUk9DIGN1cnZlLCBpcyBhIGdyYXBoaWNhbCBwbG90IHRoYXQgaWxsdXN0cmF0ZXMgdGhlIGRpYWdub3N0aWMgYWJpbGl0eSBvZiBhIGJpbmFyeSBjbGFzc2lmaWVyIHN5c3RlbSBhcyBpdHMgZGlzY3JpbWluYXRpb24gdGhyZXNob2xkIGlzIHZhcmllZC4gSXQgcXVhbnRpZmllcyB0aGUgdHJhZGVvZmYgd2UncmUgbWFraW5nIGJldHdlZW4gdGhlIFRQUiAoVHJ1ZSBQb3NpdGl2ZSBSYXRlKSBhbmQgdGhlIGZhbHNlIHBvc2l0aXZlIHJhdGUgKEZQUikgYXQgdmFyaW91cyBjdXRvZmYgc2V0dGluZ3MgKCBiZXR3ZWVuIDAgYW5kIDEgKS4gQXMgVFBSIGluY3JlYXNlcywgRlBSIGluY3JlYXNlcyB0b28sIHNvIHdlIHdhbnQgdG8gcmVhY2ggdGhlIGhpZ2hlc3QgVFBSIGJlZm9yZSB0aGUgRlBSIGluY3JlYXNlcyB0b28gbXVjaC4gVGhpcyBtZWFucyB0aGF0IHdlIHdhbnQgb3VyIEFVQyB0byBiZSByZWFsbHkgY2xvc2UgdG8gMS4KYGBge3J9CmxpYnJhcnkoJ1JPQ1InKQojIyMjIyMjIyMjIyMKIyBST0MgQ1VSVkUjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMjIyMjIyMjIyMKcHJlZCA8LSBwcmVkaWN0aW9uKHByb2IuZ2xtMS5vdXRzYW1wbGUsIGRmLnRlc3QkSXNDYW5jZWxlZCkKcGVyZiA8LSBwZXJmb3JtYW5jZShwcmVkLCAidHByIiwgImZwciIpCnJvY3IuYXVjLmxyID0gYXMubnVtZXJpYyhwZXJmb3JtYW5jZShwcmVkLCAiYXVjIilAeS52YWx1ZXMpCnBsb3QocGVyZiwgY29sb3JpemUgPSBUUlVFLAogICAgICBtYWluID0gJ1JPQyBDdXJ2ZScpCm10ZXh0KHBhc3RlKCdMb2dpc3RpYyBSZWdyZXNzaW9uIC0gYXVjIDogJywgcm91bmQocm9jci5hdWMubHIsIDUpKSkKYGBgCmBgYHtyfQojR2V0IHRoZSBBVUMKdW5saXN0KHNsb3QocGVyZm9ybWFuY2UocHJlZCwgImF1YyIpLCAieS52YWx1ZXMiKSkKIyAKYGBgCmBgYHtyfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIE9ERFMgT0YgQ0FOQ0VMRUQgQkFTRUQgT04gV0VJR0hUUyhDT0VGRklDSUVOVFMpIEdJVkVOIFRPIFZBUklBQkxFUyMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBIb3cgb2RkcyBvZiBkZWZhdWx0IGNoYW5nZSBieSBjaGFuZ2luZyBvbmUgdW5pdCBvZiB0aGUgdmFyaWFibGUuIAp0ZW1wX2NvbXBhcmUgPC0gYXMuZGF0YS5mcmFtZShleHAoY2JpbmQoT1IgPSBjb2VmKGRmLmdsbTEpKSkpCnRlbXBfY29tcGFyZQpsaWJyYXJ5KGRhdGEudGFibGUpCnNldERUKHRlbXBfY29tcGFyZSwga2VlcC5yb3duYW1lcyA9IFRSVUUpW10KdGVtcF9jb21wYXJlIDwtIHRlbXBfY29tcGFyZVtvcmRlcih0ZW1wX2NvbXBhcmUkT1IsZGVjcmVhc2luZyA9IFRSVUUpLF0KdGVtcF9jb21wYXJlIDwtIHRlbXBfY29tcGFyZVshaXMuaW5maW5pdGUodGVtcF9jb21wYXJlJE9SKV0KdGVtcF9jb21wYXJlIDwtIHRlbXBfY29tcGFyZVshaXMubmEodGVtcF9jb21wYXJlJE9SKV0KYGBgCiMjIyMgTGFzc28gZm9yIEZlYXR1cmUgU2VsZWN0aW9uCmBgYHtyfQpsaWJyYXJ5KGdsbW5ldCkKZGYuTGFzc28gPC0gZ2xtbmV0KGRhdG9zX3RyYWluX3gsIGFzLmZhY3RvcihkYXRvc190cmFpbl95KSwgYWxwaGEgPSAxLjAsIGZhbWlseSA9ICJiaW5vbWlhbCIpCnByb2IgPC0gcHJlZGljdChkZi5MYXNzbywgbmV3eCA9IGRhdG9zX3RyYWluX3gsIHR5cGUgPSAicmVzcG9uc2UiKQpyZXMgPC0gYygpCmZvciAoaiBpbiAxOm5jb2wocHJvYikpewogIGZvciAoaSBpbiAxOmxlbmd0aChzZWFyY2hncmlkKSkKICB7CiAgICBwY3V0IDwtIHJlc3VsdFtpLDFdCiAgICAjYXNzaWduIHRoZSBjb3N0IHRvIHRoZSAybmQgY29sCiAgICByZXN1bHRbaSwyXSA8LSBjb3N0MShkZi50cmFpbiRJc0NhbmNlbGVkLCBwcm9iWyxqXSkKICB9CiMjcGxvdChyZXN1bHQsIHlsYWIgPSAiQ29zdCBpbiBUcmFpbmluZyBTZXQiKQpyZXMgPC0gYyhyZXMsYXMuZG91YmxlKHJlc3VsdFt3aGljaC5taW4ocmVzdWx0WywyXSksXVsyXSkpCn0Kd2hpY2gocmVzID09IG1pbihyZXMpKVsxXQpyZXN1bHQgPSBjYmluZChzZWFyY2hncmlkLCBOQSkKZm9yIChpIGluIDE6bGVuZ3RoKHNlYXJjaGdyaWQpKQogIHsKICAgIHBjdXQgPC0gcmVzdWx0W2ksMV0KICAgICNhc3NpZ24gdGhlIGNvc3QgdG8gdGhlIDJuZCBjb2wgKHdoaWNoKHJlcyA9PSBtaW4ocmVzKSlbMV0pCiAgICByZXN1bHRbaSwyXSA8LSBjb3N0MShkZi50cmFpbiRJc0NhbmNlbGVkLCBwcm9iWyw5OF0pCiAgfQpwbG90KHJlc3VsdCwgeWxhYiA9ICJDb3N0IGluIFRyYWluaW5nIFNldCIpCnJlc3VsdFt3aGljaC5taW4ocmVzdWx0WywyXSksXQojIHNlYXJjaGdyaWQgICAgICAgICAgICAKIyAgMC40MzAxMDAwICAwLjE1MTE0ODMKYGBgCiMjIyBQUkVESUNUSU9OIFRSQUlOCmBgYHtyfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpjdXRfb2ZmIDwtIDAuNDMwMTAwMCMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKcHJvYi5MYXNzbyA8LSAgYXMuZG91YmxlKHByb2JbLDk4XSkKcHJvYi5MYXNzbyA8LSAocHJvYi5MYXNzbyA+IGN1dF9vZmYpCnByb2IuTGFzc28gPC0gYXMubnVtZXJpYyhwcm9iLkxhc3NvKQojIyMjIyMjIyMjIyMjIyMjIyMjCiMgQ09ORlVTSU9OIE1BVFJJWCMKIyMjIyMjIyMjIyMjIyMjIyMjIwp0YWJsZShkZi50cmFpbiRJc0NhbmNlbGVkLCBwcm9iLkxhc3NvLCBkbm4gPSBjKCJUcnV0aCIsIlByZWRpY3RlZCIpKQojIyMjIyMjIwojIEVSUk9SIwojIyMjIyMjIwptZWFuKGlmZWxzZShkZi50cmFpbiRJc0NhbmNlbGVkICE9IHByb2IuTGFzc28sIDEsIDApKQpgYGAKIyMjIFBSRURJQ1RJT04gVEVTVApgYGB7cn0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKY3V0X29mZiA8LSAwLjQzMDEwMDAjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnByb2IgPC0gcHJlZGljdChkZi5MYXNzbywgbmV3eCA9IGRhdG9zX3Rlc3RfeCwgdHlwZSA9ICJyZXNwb25zZSIpCnByb2IuTGFzc28gPC0gIGFzLmRvdWJsZShwcm9iWyw5OF0pCnByb2IuTGFzc28gPC0gKHByb2IuTGFzc28gPiBjdXRfb2ZmKQpwcm9iLkxhc3NvIDwtIGFzLm51bWVyaWMocHJvYi5MYXNzbykKIyMjIyMjIyMjIyMjIyMjIyMjIwojIENPTkZVU0lPTiBNQVRSSVgjCiMjIyMjIyMjIyMjIyMjIyMjIyMKdGFibGUoZGYudGVzdCRJc0NhbmNlbGVkLCBwcm9iLkxhc3NvLCBkbm4gPSBjKCJUcnV0aCIsIlByZWRpY3RlZCIpKQojIyMjIyMjIwojIEVSUk9SIwojIyMjIyMjIwptZWFuKGlmZWxzZShkZi50ZXN0JElzQ2FuY2VsZWQgIT0gcHJvYi5MYXNzbywgMSwgMCkpCgoKbWVhbihpZmVsc2UoZGYudHJhaW4kSXNDYW5jZWxlZCA9PSBwcm9iLkxhc3NvLCAxLCAwKSkKYGBgCiMjIyBST0MgQ1VSVkUKT3VyIEFyZWEgVW5kZXIgdGhlIEN1cnZlIGlzIApST0MgY3VydmUsIGlzIGEgZ3JhcGhpY2FsIHBsb3QgdGhhdCBpbGx1c3RyYXRlcyB0aGUgZGlhZ25vc3RpYyBhYmlsaXR5IG9mIGEgYmluYXJ5IGNsYXNzaWZpZXIgc3lzdGVtIGFzIGl0cyBkaXNjcmltaW5hdGlvbiB0aHJlc2hvbGQgaXMgdmFyaWVkLiBJdCBxdWFudGlmaWVzIHRoZSB0cmFkZW9mZiB3ZSdyZSBtYWtpbmcgYmV0d2VlbiB0aGUgVFBSIChUcnVlIFBvc2l0aXZlIFJhdGUpIGFuZCB0aGUgZmFsc2UgcG9zaXRpdmUgcmF0ZSAoRlBSKSBhdCB2YXJpb3VzIGN1dG9mZiBzZXR0aW5ncyAoIGJldHdlZW4gMCBhbmQgMSApLiBBcyBUUFIgaW5jcmVhc2VzLCBGUFIgaW5jcmVhc2VzIHRvbywgc28gd2Ugd2FudCB0byByZWFjaCB0aGUgaGlnaGVzdCBUUFIgYmVmb3JlIHRoZSBGUFIgaW5jcmVhc2VzIHRvbyBtdWNoLiBUaGlzIG1lYW5zIHRoYXQgd2Ugd2FudCBvdXIgQVVDIHRvIGJlIHJlYWxseSBjbG9zZSB0byAxLgpgYGB7cn0KIyMjIyMjIyMjIyMjCiMgUk9DIENVUlZFIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMjIyMjIyMjIyMjCnByZWQgPC0gcHJlZGljdGlvbihwcm9iLkxhc3NvLCBkZi50ZXN0JElzQ2FuY2VsZWQpCnBlcmYgPC0gcGVyZm9ybWFuY2UocHJlZCwgInRwciIsICJmcHIiKQpyb2NyLmF1Yy5sciA9IGFzLm51bWVyaWMocGVyZm9ybWFuY2UocHJlZCwgImF1YyIpQHkudmFsdWVzKQpwbG90KHBlcmYsIGNvbG9yaXplID0gVFJVRSwKICAgICAgbWFpbiA9ICdST0MgQ3VydmUnKQptdGV4dChwYXN0ZSgnTG9naXN0aWMgUmVncmVzc2lvbiAtIGF1YyA6ICcsIHJvdW5kKHJvY3IuYXVjLmxyLCA1KSkpCmBgYApgYGB7cn0KI0dldCB0aGUgQVVDCnVubGlzdChzbG90KHBlcmZvcm1hbmNlKHByZWQsICJhdWMiKSwgInkudmFsdWVzIikpCiMgCmBgYApgYGB7cn0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBPRERTIE9GIERFRkFVTFQgQkFTRUQgT04gV0VJR0hUUyhDT0VGRklDSUVOVFMpIEdJVkVOIFRPIFZBUklBQkxFUyMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBIb3cgb2RkcyBvZiBkZWZhdWx0IGNoYW5nZSBieSBjaGFuZ2luZyBvbmUgdW5pdCBvZiB0aGUgdmFyaWFibGUuCnRlbXBfY29tcGFyZUwgPC0gYXMuZGF0YS5mcmFtZSgoY2JpbmQoT1IgPSBjb2VmKGRmLkxhc3NvKVssMTBdKSkpCnRlbXBfY29tcGFyZUwKc2V0RFQodGVtcF9jb21wYXJlTCwga2VlcC5yb3duYW1lcyA9IFRSVUUpW10KdGVtcF9jb21wYXJlTCA8LSB0ZW1wX2NvbXBhcmVMW29yZGVyKHRlbXBfY29tcGFyZUwkT1IsZGVjcmVhc2luZyA9IFRSVUUpLF0KdGVtcF9jb21wYXJlTCA8LSB0ZW1wX2NvbXBhcmVMWyFpcy5pbmZpbml0ZSh0ZW1wX2NvbXBhcmVMJE9SKV0KdGVtcF9jb21wYXJlTCA8LSB0ZW1wX2NvbXBhcmVMWyFpcy5uYSh0ZW1wX2NvbXBhcmVMJE9SKV0KdGVtcF9jb21wYXJlTApgYGAKCgoKRWwgdGVyY2VyIGRvY3VtZW50bywgZGUgZXh0ZW5zacOzbiBtw6F4aW1hIDYgcHAsIHJlc29sdmVyw6EgdW4gcHJvYmxlbWEgZGUgcHJlZGljY2nDs24gZGUgc2VyaWVzIHRlbXBvcmFsZXMuIEVuIHByaW1lciBsdWdhciwgc2UgZGViZSBlbGFib3JhciBsYSBzZXJpZSBzZW1hbmFsIGRlIHJlc2VydmFzIHJlYWxpemFkYXMuIFNlIGRlYmVyw6EgcGxhbnRlYXIgdW4gbW9kZWxvIGFkZWN1YWRvIHBhcmEgcHJlZGVjaXIgZXN0YSBzZXJpZSB0ZW1wb3JhbC4KCiMjIFRJTUUgU0VSSUVTIEFOQUxZU0lTCiAKVGltZSBTZXJpZXM6CkhPVEVMJ1MgTUFOQUdFTUVOVAogIC0gTnVtYmVyIG9mIFJlc2VydmF0aW9ucyAtIEFycml2YWxEYXRlCiAgLSBOdW1iZXIgb2YgUmVzZXJ2YXRpb25zIHRoYXQgd2lsbCBDaGVja091dCAtIEFycml2YWxEYXRlCiAgLSBOdW1iZXIgb2YgUmVzZXJ2YXRpb25zIHRoYXQgd2lsbCBub3QgQ2hlY2tPdXQgLSBBcnJpdmFsRGF0ZQogIApIT1RFTCdTIE1BUktFVElORyAgCiAgLSBOdW1iZXIgb2YgUmVzZXJ2YXRpb25zIHRoYXQgd2lsbCBDaGVja091dCAtIFJlc2VydmF0aW9uRGF0ZQogIC0gTnVtYmVyIG9mIFJlc2VydmF0aW9ucyB0aGF0IHdpbGwgbm90IENoZWNrT3V0LSBSZXNlcnZhdGlvbkRhdGUKICAKCiMjIyMgREFUQSBQUkUtUFJPQ0VTU0lORwoKLSBXZSBvYnRhaW4gQXJyaXZhbERhdGUgZnJvbSBkaWZmZXJlbnQgQ29sdW1ucy4KCgpgYGB7cn0KZXJhc2VfY29sdW1ucyA8LSBjKDEyLCAxMywgMTQsIDE1LCAxOSwgMjAsIDIyLCAyNiwgMzApCnRpbWVTZXJpZXNEYXRhU2V0IDwtIHRpbWVTZXJpZXNEYXRhU2V0WyAtZXJhc2VfY29sdW1ucyBdCmBgYAoKIyMjIyBFWFBMT1JBVE9SWSBEQVRBIEFOQUxZU0lTIC0gVElNRSBTRVJJRVMKCiMjIyMjIFRJTUUgU0VSSUVTIEZJUlNUIFZJRVcKIyMjIyMjIEFycml2YWwgRGF0ZQpgYGB7cn0KeWVhck1vbnRoRGF5IDwtIGZ1bmN0aW9uKHksIG0sIGQpewogIGRhdGUgPC0gcGFzdGUoYXMuY2hhcmFjdGVyKHkpLHBhc3RlKG0sIGQsIHNlcCA9ICItIiksIHNlcCA9ICItIikKICBkYXRlIDwtIGFzLkRhdGUoZGF0ZSwgIiVZLSVtLSVkIikKfQphcnJpdmFsX2RhdGUgPC0geWVhck1vbnRoRGF5KHkgPSB0aW1lU2VyaWVzRGF0YVNldCRBcnJpdmFsRGF0ZVllYXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbSA9IHRpbWVTZXJpZXNEYXRhU2V0JEFycml2YWxEYXRlTW9udGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZCA9IHRpbWVTZXJpZXNEYXRhU2V0JEFycml2YWxEYXRlRGF5T2ZNb250aCkKCmFycml2YWxfZGF0ZSA8LSBhcy5kYXRhLmZyYW1lKGFycml2YWxfZGF0ZSkgCgphID0gYXJyaXZhbF9kYXRlICU+JSBncm91cF9ieShhcnJpdmFsX2RhdGUpICU+JSBzdW1tYXJpc2UobiA9IG4oKSkKZGF0YSA8LSBhcy54dHMoYSRuLG9yZGVyLmJ5PWFzLkRhdGUoYSRhcnJpdmFsX2RhdGUpKQp3ZWVrbHkgPC0gYXBwbHkud2Vla2x5KGRhdGEsc3VtKQp3ZWVrbHkgPC0gYXMuZGF0YS5mcmFtZSh3ZWVrbHkpCgojaW5kZXggb2YgYSBkYXRhZnJhbWUgdG8gY29sdW1ucwpsaWJyYXJ5KGRhdGEudGFibGUpCnNldERUKHdlZWtseSwga2VlcC5yb3duYW1lcyA9IFRSVUUpW10Kd2Vla2x5JHJuIDwtIGFzLkRhdGUod2Vla2x5JHJuKQphcnJpdmFsRGF0ZVBsb3QgPC0gd2Vla2x5CmdncGxvdChkYXRhID0gd2Vla2x5LAogICAgICBhZXMoeCA9IHJuKSkrCiAgICAgIGdlb21fbGluZShhZXMoeSA9IFYxLCBjb2xvciA9ICJOdW1iZXIgb2YgUmVzZXJ2YXRpb25zIGZvciBBcnJpdmFsRGF0ZSIpKSsKICAgICAgeWxhYignTnVtYmVyIG9mIFJlc2VydmF0aW9ucyBmb3IgdGhhdCBBcnJpdmFsIERhdGUnKSsKICAgICAgeGxhYignRGF0ZSAtIFdlZWtseScpKwogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAojIyMjIyMgUmVzZXJ2YXRpb24gRGF0ZQotIFJlc2VydmF0aW9uRGF0ZSA9IEFycml2YWxEYXRlIC0gTGVhZFRpbWUKYGBge3J9CnllYXJNb250aERheSA8LSBmdW5jdGlvbih5LCBtLCBkKXsKICBkYXRlIDwtIHBhc3RlKGFzLmNoYXJhY3Rlcih5KSxwYXN0ZShtLCBkLCBzZXAgPSAiLSIpLCBzZXAgPSAiLSIpCiAgZGF0ZSA8LSBhcy5EYXRlKGRhdGUsICIlWS0lbS0lZCIpCn0KYXJyaXZhbF9kYXRlIDwtIHllYXJNb250aERheSh5ID0gdGltZVNlcmllc0RhdGFTZXQkQXJyaXZhbERhdGVZZWFyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG0gPSB0aW1lU2VyaWVzRGF0YVNldCRBcnJpdmFsRGF0ZU1vbnRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGQgPSB0aW1lU2VyaWVzRGF0YVNldCRBcnJpdmFsRGF0ZURheU9mTW9udGgpCnJlc2VydmF0aW9uRGF0ZSA8LSBhcnJpdmFsX2RhdGUgLSB0aW1lU2VyaWVzRGF0YVNldCRMZWFkVGltZQpyZXNlcnZhdGlvbkRhdGUgPC0gYXMuZGF0YS5mcmFtZShyZXNlcnZhdGlvbkRhdGUpIAoKIyByZXNlcnZhdGlvbkRhdGUgPC0gcmVzZXJ2YXRpb25EYXRlJT4lcmVuYW1lKCAncmVzZXJ2YXRpb25EYXRlJyA9ICdhcnJpdmFsX2RhdGUnICkKCiAgICAgICAgIAphID0gcmVzZXJ2YXRpb25EYXRlICU+JSBncm91cF9ieShyZXNlcnZhdGlvbkRhdGUpICU+JSBzdW1tYXJpc2UobiA9IG4oKSkKZGF0YSA8LSBhcy54dHMoYSRuLG9yZGVyLmJ5PWFzLkRhdGUoYSRyZXNlcnZhdGlvbkRhdGUpKQp3ZWVrbHkgPC0gYXBwbHkud2Vla2x5KGRhdGEsc3VtKQp3ZWVrbHkgPC0gYXMuZGF0YS5mcmFtZSh3ZWVrbHkpCgojaW5kZXggb2YgYSBkYXRhZnJhbWUgdG8gY29sdW1ucwpsaWJyYXJ5KGRhdGEudGFibGUpCnNldERUKHdlZWtseSwga2VlcC5yb3duYW1lcyA9IFRSVUUpW10Kd2Vla2x5JHJuIDwtIGFzLkRhdGUod2Vla2x5JHJuKQpyZXNlcnZhdGlvbkRhdGVQbG90IDwtIHdlZWtseQpnZ3Bsb3QoZGF0YSA9IHdlZWtseSwKICAgICAgYWVzKHggPSBybikpKwogICAgICBnZW9tX2xpbmUoYWVzKHkgPSBWMSwgY29sb3IgPSAiTnVtYmVyIG9mIFJlc2VydmF0aW9ucyBmb3IgUmVzZXJ2YXRpb24gRGF0ZSIpKSsKICAgICAgeWxhYignTnVtYmVyIG9mIFJlc2VydmF0aW9ucyBmb3IgdGhhdCBSZXNlcnZhdGlvbiBEYXRlJykrCiAgICAgIHhsYWIoJ0RhdGUgLSBXZWVrbHknKSsKICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKIyMjIyMjIEFycml2YWwgYW5kIFJlc2VydmF0aW9uIERhdGUKYGBge3J9CmdncGxvdCgpICsKICBnZW9tX2xpbmUoZGF0YSA9IGFycml2YWxEYXRlUGxvdCwgYWVzKHggPSBybiwgeSA9IFYxLCBjb2wgPSAiQXJyaXZhbCBEYXRlIikpICsgCiAgZ2VvbV9saW5lKGRhdGEgPSByZXNlcnZhdGlvbkRhdGVQbG90LCBhZXMoeCA9IHJuLCB5ID0gVjEsIGNvbCA9ICJSZXNlcnZhdGlvbiBEYXRlIikpKwogIHhsYWIoJ0RhdGUgLSBXZWVrcycpKwogIHlsYWIoJ051bWJlciBvZiBSZXNlcnZhdGlvbnMnKSsKICBnZ3RpdGxlKCdOdW1iZXIgb2YgUmVzZXJ2YXRpb25zJykKYGBgCgpgYGB7cn0Kbm9uQ2hlY2tvdXRUUzwtdGltZVNlcmllc0RhdGFTZXQlPiVmaWx0ZXIoSXNDYW5jZWxlZCA9PSAxKQpjaGVja291dFRTIDwtdGltZVNlcmllc0RhdGFTZXQlPiVmaWx0ZXIoSXNDYW5jZWxlZCA9PSAwKQpgYGAKCgojIyMjIyMgQXJyaXZhbCBEYXRlIENoZWNrb3V0CmBgYHtyfQoKYXJyaXZhbF9kYXRlQ2hlY2tvdXQgPC0geWVhck1vbnRoRGF5KHkgPSBjaGVja291dFRTJEFycml2YWxEYXRlWWVhciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtID0gY2hlY2tvdXRUUyRBcnJpdmFsRGF0ZU1vbnRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGQgPSBjaGVja291dFRTJEFycml2YWxEYXRlRGF5T2ZNb250aCkKCmFycml2YWxfZGF0ZUNoZWNrb3V0IDwtIGFzLmRhdGEuZnJhbWUoYXJyaXZhbF9kYXRlQ2hlY2tvdXQpIAoKYSA9IGFycml2YWxfZGF0ZUNoZWNrb3V0ICU+JSBncm91cF9ieShhcnJpdmFsX2RhdGVDaGVja291dCkgJT4lIHN1bW1hcmlzZShuID0gbigpKQpkYXRhIDwtIGFzLnh0cyhhJG4sb3JkZXIuYnk9YXMuRGF0ZShhJGFycml2YWxfZGF0ZUNoZWNrb3V0KSkKd2Vla2x5IDwtIGFwcGx5LndlZWtseShkYXRhLHN1bSkKd2Vla2x5IDwtIGFzLmRhdGEuZnJhbWUod2Vla2x5KQoKI2luZGV4IG9mIGEgZGF0YWZyYW1lIHRvIGNvbHVtbnMKbGlicmFyeShkYXRhLnRhYmxlKQpzZXREVCh3ZWVrbHksIGtlZXAucm93bmFtZXMgPSBUUlVFKVtdCndlZWtseSRybiA8LSBhcy5EYXRlKHdlZWtseSRybikKYXJyaXZhbERhdGVQbG90Q2hlY2tvdXQgPC0gd2Vla2x5CmdncGxvdChkYXRhID0gd2Vla2x5LAogICAgICBhZXMoeCA9IHJuKSkrCiAgICAgIGdlb21fbGluZShhZXMoeSA9IFYxLCBjb2xvciA9ICJOdW1iZXIgb2YgUmVzZXJ2YXRpb25zIGZvciBBcnJpdmFsRGF0ZSBvZiBwZW9wbGUgd2hvIENoZWNrb3V0IikpKwogICAgICB5bGFiKCdOdW1iZXIgb2YgUmVzZXJ2YXRpb25zIGZvciB0aGF0IEFycml2YWwgRGF0ZSBvZiBwZW9wbGUgd2hvIENoZWNrb3V0JykrCiAgICAgIHhsYWIoJ0RhdGUgLSBXZWVrbHknKSsKICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCgojIyMjIyMgQXJyaXZhbCBEYXRlIENhbmNlbGVkCmBgYHtyfQoKYXJyaXZhbF9kYXRlQ2FuY2VsZWQgPC0geWVhck1vbnRoRGF5KHkgPSBub25DaGVja291dFRTJEFycml2YWxEYXRlWWVhciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtID0gbm9uQ2hlY2tvdXRUUyRBcnJpdmFsRGF0ZU1vbnRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGQgPSBub25DaGVja291dFRTJEFycml2YWxEYXRlRGF5T2ZNb250aCkKCmFycml2YWxfZGF0ZUNhbmNlbGVkIDwtIGFzLmRhdGEuZnJhbWUoYXJyaXZhbF9kYXRlQ2FuY2VsZWQpIAoKYSA9IGFycml2YWxfZGF0ZUNhbmNlbGVkICU+JSBncm91cF9ieShhcnJpdmFsX2RhdGVDYW5jZWxlZCkgJT4lIHN1bW1hcmlzZShuID0gbigpKQpkYXRhIDwtIGFzLnh0cyhhJG4sb3JkZXIuYnk9YXMuRGF0ZShhJGFycml2YWxfZGF0ZUNhbmNlbGVkKSkKd2Vla2x5IDwtIGFwcGx5LndlZWtseShkYXRhLHN1bSkKd2Vla2x5IDwtIGFzLmRhdGEuZnJhbWUod2Vla2x5KQoKI2luZGV4IG9mIGEgZGF0YWZyYW1lIHRvIGNvbHVtbnMKbGlicmFyeShkYXRhLnRhYmxlKQpzZXREVCh3ZWVrbHksIGtlZXAucm93bmFtZXMgPSBUUlVFKVtdCndlZWtseSRybiA8LSBhcy5EYXRlKHdlZWtseSRybikKYXJyaXZhbERhdGVQbG90Q2FuY2VsZWQgPC0gd2Vla2x5CmdncGxvdChkYXRhID0gd2Vla2x5LAogICAgICBhZXMoeCA9IHJuKSkrCiAgICAgIGdlb21fbGluZShhZXMoeSA9IFYxLCBjb2xvciA9ICJOdW1iZXIgb2YgUmVzZXJ2YXRpb25zIGZvciBBcnJpdmFsRGF0ZSBvZiBwZW9wbGUgd2hvIENoZWNrb3V0IikpKwogICAgICB5bGFiKCdOdW1iZXIgb2YgUmVzZXJ2YXRpb25zIGZvciB0aGF0IEFycml2YWwgRGF0ZSBvZiBwZW9wbGUgd2hvIENoZWNrb3V0JykrCiAgICAgIHhsYWIoJ0RhdGUgLSBXZWVrbHknKSsKICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCgojIyMjIyMgQXJyaXZhbCBEYXRlIENoZWNrb3V0IGFuZCBDYW5jZWxlZApgYGB7cn0KZ2dwbG90KCkgKwogIGdlb21fbGluZShkYXRhID0gYXJyaXZhbERhdGVQbG90Q2hlY2tvdXQsIGFlcyh4ID0gcm4sIHkgPSBWMSwgY29sID0gIkFycml2YWwgRGF0ZSBDaGVja291dCIpKSArIAogIGdlb21fbGluZShkYXRhID0gYXJyaXZhbERhdGVQbG90Q2FuY2VsZWQsIGFlcyh4ID0gcm4sIHkgPSBWMSwgY29sID0gIkFycml2YWwgRGF0ZSBDYW5jZWxlZCIpKSsKICB4bGFiKCdEYXRlIC0gV2Vla3MnKSsKICB5bGFiKCdOdW1iZXIgb2YgUmVzZXJ2YXRpb25zJykrCiAgZ2d0aXRsZSgnTnVtYmVyIG9mIFJlc2VydmF0aW9ucycpCmBgYAoKCgojIyMjIyMgUmVzZXJ2YXRpb24gRGF0ZSBDYW5jZWxlZApgYGB7cn0KcmVzZXJ2ZXRpb25fZGF0ZU5vbkNoZWNrb3V0IDwtIHllYXJNb250aERheSh5ID0gbm9uQ2hlY2tvdXRUUyRBcnJpdmFsRGF0ZVllYXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbSA9IG5vbkNoZWNrb3V0VFMkQXJyaXZhbERhdGVNb250aCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkID0gbm9uQ2hlY2tvdXRUUyRBcnJpdmFsRGF0ZURheU9mTW9udGgpCnJlc2VydmV0aW9uX2RhdGVOb25DaGVja291dCA8LSByZXNlcnZldGlvbl9kYXRlTm9uQ2hlY2tvdXQgLSBub25DaGVja291dFRTJExlYWRUaW1lCgpyZXNlcnZldGlvbl9kYXRlTm9uQ2hlY2tvdXQgPC0gYXMuZGF0YS5mcmFtZShyZXNlcnZldGlvbl9kYXRlTm9uQ2hlY2tvdXQpIAoKYSA9IHJlc2VydmV0aW9uX2RhdGVOb25DaGVja291dCAlPiUgZ3JvdXBfYnkocmVzZXJ2ZXRpb25fZGF0ZU5vbkNoZWNrb3V0KSAlPiUgc3VtbWFyaXNlKG4gPSBuKCkpCmRhdGEgPC0gYXMueHRzKGEkbixvcmRlci5ieT1hcy5EYXRlKGEkcmVzZXJ2ZXRpb25fZGF0ZU5vbkNoZWNrb3V0KSkKd2Vla2x5IDwtIGFwcGx5LndlZWtseShkYXRhLHN1bSkKd2Vla2x5IDwtIGFzLmRhdGEuZnJhbWUod2Vla2x5KQoKI2luZGV4IG9mIGEgZGF0YWZyYW1lIHRvIGNvbHVtbnMKbGlicmFyeShkYXRhLnRhYmxlKQpzZXREVCh3ZWVrbHksIGtlZXAucm93bmFtZXMgPSBUUlVFKVtdCndlZWtseSRybiA8LSBhcy5EYXRlKHdlZWtseSRybikKcmVzZXJ2ZXRpb25EYXRlUGxvdENhbmNlbGVkIDwtIHdlZWtseQpnZ3Bsb3QoZGF0YSA9IHdlZWtseSwKICAgICAgYWVzKHggPSBybikpKwogICAgICBnZW9tX2xpbmUoYWVzKHkgPSBWMSwgY29sb3IgPSAiTnVtYmVyIG9mIFJlc2VydmF0aW9ucyBSZXNlcnZhdGlvbkRhdGUgQ2FuY2VsZWQiKSkrCiAgICAgIHlsYWIoJ051bWJlciBvZiBSZXNlcnZhdGlvbnMgUmVzZXJ2YXRpb25EYXRlIENhbmNlbGVkJykrCiAgICAgIHhsYWIoJ0RhdGUgLSBXZWVrbHknKSsKICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiMjIyMjIyBSZXNlcnZhdGlvbiBEYXRlIENoZWNrb3V0CmBgYHtyfQpyZXNlcnZldGlvbl9kYXRlQ2hlY2tvdXQgPC0geWVhck1vbnRoRGF5KHkgPSBjaGVja291dFRTJEFycml2YWxEYXRlWWVhciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtID0gY2hlY2tvdXRUUyRBcnJpdmFsRGF0ZU1vbnRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGQgPSBjaGVja291dFRTJEFycml2YWxEYXRlRGF5T2ZNb250aCkKcmVzZXJ2ZXRpb25fZGF0ZUNoZWNrb3V0IDwtIHJlc2VydmV0aW9uX2RhdGVDaGVja291dCAtIGNoZWNrb3V0VFMkTGVhZFRpbWUKCnJlc2VydmV0aW9uX2RhdGVDaGVja291dCA8LSBhcy5kYXRhLmZyYW1lKHJlc2VydmV0aW9uX2RhdGVDaGVja291dCkgCgphID0gcmVzZXJ2ZXRpb25fZGF0ZUNoZWNrb3V0ICU+JSBncm91cF9ieShyZXNlcnZldGlvbl9kYXRlQ2hlY2tvdXQpICU+JSBzdW1tYXJpc2UobiA9IG4oKSkKZGF0YSA8LSBhcy54dHMoYSRuLG9yZGVyLmJ5PWFzLkRhdGUoYSRyZXNlcnZldGlvbl9kYXRlQ2hlY2tvdXQpKQp3ZWVrbHkgPC0gYXBwbHkud2Vla2x5KGRhdGEsc3VtKQp3ZWVrbHkgPC0gYXMuZGF0YS5mcmFtZSh3ZWVrbHkpCgojaW5kZXggb2YgYSBkYXRhZnJhbWUgdG8gY29sdW1ucwpsaWJyYXJ5KGRhdGEudGFibGUpCnNldERUKHdlZWtseSwga2VlcC5yb3duYW1lcyA9IFRSVUUpW10Kd2Vla2x5JHJuIDwtIGFzLkRhdGUod2Vla2x5JHJuKQpyZXNlcnZldGlvbkRhdGVQbG90Q2hlY2tvdXQgPC0gd2Vla2x5CmdncGxvdChkYXRhID0gd2Vla2x5LAogICAgICBhZXMoeCA9IHJuKSkrCiAgICAgIGdlb21fbGluZShhZXMoeSA9IFYxLCBjb2xvciA9ICJOdW1iZXIgb2YgUmVzZXJ2YXRpb25zIFJlc2VydmF0aW9uRGF0ZSBDaGVja291dCIpKSsKICAgICAgeWxhYignTnVtYmVyIG9mIFJlc2VydmF0aW9ucyBSZXNlcnZhdGlvbkRhdGUgQ2hlY2tvdXQnKSsKICAgICAgeGxhYignRGF0ZSAtIFdlZWtseScpKwogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKIyMjIyMjIFJlc2VydmF0aW9uIERhdGUgQ2hlY2tvdXQgYW5kIENhbmNlbGVkCmBgYHtyfQpnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGRhdGEgPSByZXNlcnZldGlvbkRhdGVQbG90Q2hlY2tvdXQsIGFlcyh4ID0gcm4sIHkgPSBWMSwgY29sID0gIlJlc2VydmF0aW9uIERhdGUgQ2hlY2tvdXQiKSkgKyAKICBnZW9tX2xpbmUoZGF0YSA9IHJlc2VydmV0aW9uRGF0ZVBsb3RDYW5jZWxlZCwgYWVzKHggPSBybiwgeSA9IFYxLCBjb2wgPSAiUmVzZXJ2ZXRpb24gRGF0ZSBDYW5jZWxlZCIpKSsKICB4bGFiKCdEYXRlIC0gV2Vla3MnKSsKICB5bGFiKCdOdW1iZXIgb2YgUmVzZXJ2YXRpb25zJykrCiAgZ2d0aXRsZSgnTnVtYmVyIG9mIFJlc2VydmF0aW9ucycpCmBgYAoKIyMjIyMjIEFycml2YWwgRGF0ZSBmb3IgQURSCmBgYHtyfQp5ZWFyTW9udGhEYXkgPC0gZnVuY3Rpb24oeSwgbSwgZCl7CiAgZGF0ZSA8LSBwYXN0ZShhcy5jaGFyYWN0ZXIoeSkscGFzdGUobSwgZCwgc2VwID0gIi0iKSwgc2VwID0gIi0iKQogIGRhdGUgPC0gYXMuRGF0ZShkYXRlLCAiJVktJW0tJWQiKQp9CmFycml2YWxfZGF0ZSA8LSB5ZWFyTW9udGhEYXkoeSA9IHRpbWVTZXJpZXNEYXRhU2V0JEFycml2YWxEYXRlWWVhciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtID0gdGltZVNlcmllc0RhdGFTZXQkQXJyaXZhbERhdGVNb250aCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkID0gdGltZVNlcmllc0RhdGFTZXQkQXJyaXZhbERhdGVEYXlPZk1vbnRoKQoKYXJyaXZhbF9kYXRlQWRyPC0gYXMuZGF0YS5mcmFtZShhcnJpdmFsX2RhdGUpIAphcnJpdmFsX2RhdGVBZHIkQURSIDwtIHJhd19kYXRhJEFEUgpjb2xuYW1lcyhhcnJpdmFsX2RhdGVBZHIpIDwtIGMoImFycml2YWxfZGF0ZSIsICJBRFIiKQoKYSA9IGFycml2YWxfZGF0ZUFkciAlPiUgZ3JvdXBfYnkoYXJyaXZhbF9kYXRlKSAlPiUgc3VtbWFyaXplKHRvdGFsTWVhbj1tZWFuKEFEUikpCgpkYXRhIDwtIGFzLnh0cyhhJHRvdGFsTWVhbixvcmRlci5ieT1hcy5EYXRlKGEkYXJyaXZhbF9kYXRlKSkKd2Vla2x5IDwtIGFwcGx5LndlZWtseShkYXRhLG1lYW4pCndlZWtseSA8LSBhcy5kYXRhLmZyYW1lKHdlZWtseSkKCiNpbmRleCBvZiBhIGRhdGFmcmFtZSB0byBjb2x1bW5zCmxpYnJhcnkoZGF0YS50YWJsZSkKc2V0RFQod2Vla2x5LCBrZWVwLnJvd25hbWVzID0gVFJVRSlbXQp3ZWVrbHkkcm4gPC0gYXMuRGF0ZSh3ZWVrbHkkcm4pCmFycml2YWxEYXRlUGxvdEFkciA8LSB3ZWVrbHkKZ2dwbG90KGRhdGEgPSB3ZWVrbHksCiAgICAgIGFlcyh4ID0gcm4pKSsKICAgICAgZ2VvbV9saW5lKGFlcyh5ID0gVjEsIGNvbG9yID0gIkFEUiBmb3IgQXJyaXZhbERhdGUiKSkrCiAgICAgIHlsYWIoJ0FEUiBmb3IgdGhhdCBBcnJpdmFsIERhdGUnKSsKICAgICAgeGxhYignRGF0ZSAtIFdlZWtseScpKwogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAojIyMjIyMgQXJyaXZhbCBEYXRlLCBBcnJpdmFsIERhdGUgbm90IGNhbmNlbGVkLCBNZWFuIEFEUiBEYXRlCmBgYHtyfQpnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGRhdGEgPSBhcnJpdmFsRGF0ZVBsb3QsIGFlcyh4ID0gcm4sIHkgPSBWMSwgY29sID0gIkFycml2YWwgRGF0ZSIpKSArIAogIGdlb21fbGluZShkYXRhID0gYXJyaXZhbERhdGVQbG90QWRyLCBhZXMoeCA9IHJuLCB5ID0gVjEsIGNvbCA9ICJNZWFuIEFEUiBEYXRlIikpKwogIGdlb21fbGluZShkYXRhID0gYXJyaXZhbERhdGVQbG90Q2hlY2tvdXQsIGFlcyh4ID0gcm4sIHkgPSBWMSwgY29sID0gIkFycml2YWwgRGF0ZSBub3QgQ2FuY2VsZWQiKSkrCiAgeGxhYignRGF0ZSAtIFdlZWtzJykrCiAgeWxhYignTnVtYmVyIG9mIFJlc2VydmF0aW9ucycpKwogIGdndGl0bGUoJ051bWJlciBvZiBSZXNlcnZhdGlvbnMnKQpgYGAKCgojIyMjIyBTRUFTT05BTElUWQpgYGB7cn0KICAjVHJhbnNmb3JtIHRvIHpvbyBkYXRhIChmb3JlY2FzdCBwYWNrYWdlKQogIHpSZXNlcnZhdGlvbkRhdGVOdW1iZXI9YXMuem9vKHJlc2VydmF0aW9uRGF0ZVBsb3QkVjEpCiAgekFycml2YWxEYXRlTnVtYmVyPWFzLnpvbyhhcnJpdmFsRGF0ZVBsb3QkVjEpCiAgekFkckRhdGVOdW1iZXIgPSBhcy56b28oYXJyaXZhbERhdGVQbG90QWRyJFYxKQogIAogIHpBcnJpdmFsRGF0ZUNhbmNlbGVkID0gYXMuem9vKGFycml2YWxEYXRlUGxvdENhbmNlbGVkJFYxKQogIHpBcnJpdmFsRGF0ZUNoZWNrb3V0ID0gYXMuem9vKGFycml2YWxEYXRlUGxvdENoZWNrb3V0JFYxKQogIAogIHpSZXNlcnZhdGlvbkRhdGVDYW5jZWxlZCA9IGFzLnpvbyhyZXNlcnZldGlvbkRhdGVQbG90Q2FuY2VsZWQkVjEpIAogIHpSZXNlcnZhdGlvbkRhdGVDaGVja291dCA9IGFzLnpvbyhyZXNlcnZldGlvbkRhdGVQbG90Q2hlY2tvdXQkVjEpIAoKYGBgCgpgYGB7cn0KICNTZWFzb25hbCBQbG90CiAgZ2dmcmVxcGxvdChhcy50cyh6UmVzZXJ2YXRpb25EYXRlTnVtYmVyKSxmcmVxPTQsbnJvdz0xLGZhY2V0LmxhYmVsbGVyPWMoIjFUIiwiMlQiLCIzVCIsIjRUIikpK2dndGl0bGUoIk51bWJlciBvZiBSZXNlcnZhdGlvbnMgLSBSZXNlcnZhdGlvbiBEYXRlICIpCgogI1NlYXNvbmFsIFBsb3QKICBnZ2ZyZXFwbG90KGFzLnRzKHpBcnJpdmFsRGF0ZU51bWJlciksZnJlcT00LG5yb3c9MSxmYWNldC5sYWJlbGxlcj1jKCIxVCIsIjJUIiwiM1QiLCI0VCIpKStnZ3RpdGxlKCJOdW1iZXIgb2YgUmVzZXJ2YXRpb25zIC0gQXJyaXZhbCBEYXRlICIpCiAgCiAgI1NlYXNvbmFsIFBsb3QKICBnZ2ZyZXFwbG90KGFzLnRzKHpBZHJEYXRlTnVtYmVyKSxmcmVxPTQsbnJvdz0xLGZhY2V0LmxhYmVsbGVyPWMoIjFUIiwiMlQiLCIzVCIsIjRUIikpK2dndGl0bGUoIk1lYW4gQURSICAtIEFycml2YWwgRGF0ZSAiKQogIAogICAjU2Vhc29uYWwgUGxvdAogIGdnZnJlcXBsb3QoYXMudHMoekFycml2YWxEYXRlQ2FuY2VsZWQpLGZyZXE9NCxucm93PTEsZmFjZXQubGFiZWxsZXI9YygiMVQiLCIyVCIsIjNUIiwiNFQiKSkrZ2d0aXRsZSgiTWVhbiBBRFIgIC0gQXJyaXZhbCBEYXRlICIpCgogICNTZWFzb25hbCBQbG90CiAgZ2dmcmVxcGxvdChhcy50cyh6QXJyaXZhbERhdGVDaGVja291dCksZnJlcT00LG5yb3c9MSxmYWNldC5sYWJlbGxlcj1jKCIxVCIsIjJUIiwiM1QiLCI0VCIpKStnZ3RpdGxlKCJNZWFuIEFEUiAgLSBBcnJpdmFsIERhdGUgIikKICAKICAgICNTZWFzb25hbCBQbG90CiAgZ2dmcmVxcGxvdChhcy50cyh6UmVzZXJ2YXRpb25EYXRlQ2FuY2VsZWQpLGZyZXE9NCxucm93PTEsZmFjZXQubGFiZWxsZXI9YygiMVQiLCIyVCIsIjNUIiwiNFQiKSkrZ2d0aXRsZSgiTWVhbiBBRFIgIC0gQXJyaXZhbCBEYXRlICIpCiAgCiAgICNTZWFzb25hbCBQbG90CiAgZ2dmcmVxcGxvdChhcy50cyh6UmVzZXJ2YXRpb25EYXRlQ2hlY2tvdXQpLGZyZXE9NCxucm93PTEsZmFjZXQubGFiZWxsZXI9YygiMVQiLCIyVCIsIjNUIiwiNFQiKSkrZ2d0aXRsZSgiTWVhbiBBRFIgIC0gQXJyaXZhbCBEYXRlICIpCiAgCmBgYAoKIyMjIyMgU1RBVElPTkFSSVRZIC0gQUNGIC0gUEFDRgpMb2cgdHJhbnNmb3JtYXRpb24gYW5kIGRpZmZlcmVuY2VzCmBgYHtyICBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojTG9nIHRyYW5zZm9ybWF0aW9uPwp6bEFycml2YWxEYXRlTnVtYmVyPWxvZyh6QXJyaXZhbERhdGVOdW1iZXIpCmRmX25ld2wgPC0gZGF0YS5mcmFtZSh2YWx1ZSA9IGFzLnZlY3Rvcih6bEFycml2YWxEYXRlTnVtYmVyKSwKICAgICAgICAgICAgICAgICAgICAgdGltZSA9IHRpbWUoemxBcnJpdmFsRGF0ZU51bWJlcikpCmdncGxvdChkZl9uZXdsKStnZW9tX3BvaW50KGFlcyh4PXRpbWUseT12YWx1ZSkpK2dlb21fbGluZShhZXMoeD10aW1lLHk9dmFsdWUpKSt5bGFiKCJOdW1iZXIgb2YgUmVzZXJ2YXRpb25zIikrZ2d0aXRsZSgiTnVtYmVyIG9mIFJlc2VydmF0aW9ucyBmb3IgQXJyaXZhbCBEYXRlICBMT0cgIikreGxhYigiRGF0ZSAtIFdlZWtzIikKCiNEaWZmZXJlbmNlCmdndHNkaXNwbGF5KHpsQXJyaXZhbERhdGVOdW1iZXIpCmdndHNkaXNwbGF5KGRpZmYoemxBcnJpdmFsRGF0ZU51bWJlcikpCmdndHNkaXNwbGF5KGRpZmYoemxBcnJpdmFsRGF0ZU51bWJlciw0KSkKZ2d0c2Rpc3BsYXkoZGlmZihkaWZmKHpsQXJyaXZhbERhdGVOdW1iZXIsNCksMSkpCmBgYAoKYGBge3IgIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiNMb2cgdHJhbnNmb3JtYXRpb24/CnpsUmVzZXJ2YXRpb25EYXRlTnVtYmVyPWxvZyh6UmVzZXJ2YXRpb25EYXRlTnVtYmVyKQpkZl9uZXdsIDwtIGRhdGEuZnJhbWUodmFsdWUgPSBhcy52ZWN0b3IoelJlc2VydmF0aW9uRGF0ZU51bWJlciksCiAgICAgICAgICAgICAgICAgICAgIHRpbWUgPSB0aW1lKHpSZXNlcnZhdGlvbkRhdGVOdW1iZXIpKQpnZ3Bsb3QoZGZfbmV3bCkrZ2VvbV9wb2ludChhZXMoeD10aW1lLHk9dmFsdWUpKStnZW9tX2xpbmUoYWVzKHg9dGltZSx5PXZhbHVlKSkreWxhYigiTnVtYmVyIG9mIFJlc2VydmF0aW9ucyIpK2dndGl0bGUoIk51bWJlciBvZiBSZXNlcnZhdGlvbnMgZm9yIEFycml2YWwgRGF0ZSAgTE9HICIpK3hsYWIoIkRhdGUgLSBXZWVrcyIpCgojRGlmZmVyZW5jZQpnZ3RzZGlzcGxheSh6bFJlc2VydmF0aW9uRGF0ZU51bWJlcikKZ2d0c2Rpc3BsYXkoZGlmZih6bFJlc2VydmF0aW9uRGF0ZU51bWJlcikpCmdndHNkaXNwbGF5KGRpZmYoemxSZXNlcnZhdGlvbkRhdGVOdW1iZXIsNCkpCmdndHNkaXNwbGF5KGRpZmYoZGlmZih6bFJlc2VydmF0aW9uRGF0ZU51bWJlciw0KSwxKSkKYGBgCgpgYGB7ciAgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KI0xvZyB0cmFuc2Zvcm1hdGlvbj8KemxBZHJEYXRlTnVtYmVyPWxvZyh6QWRyRGF0ZU51bWJlcikKZGZfbmV3bCA8LSBkYXRhLmZyYW1lKHZhbHVlID0gYXMudmVjdG9yKHpBZHJEYXRlTnVtYmVyKSwKICAgICAgICAgICAgICAgICAgICAgdGltZSA9IHRpbWUoekFkckRhdGVOdW1iZXIpKQpnZ3Bsb3QoZGZfbmV3bCkrZ2VvbV9wb2ludChhZXMoeD10aW1lLHk9dmFsdWUpKStnZW9tX2xpbmUoYWVzKHg9dGltZSx5PXZhbHVlKSkreWxhYigiTWVhbiBBRFIgIikrZ2d0aXRsZSgiTWVhbiBBRFIgZm9yIEFycml2YWwgRGF0ZSAgTE9HICIpK3hsYWIoIkRhdGUgLSBXZWVrcyIpCgojRGlmZmVyZW5jZQpnZ3RzZGlzcGxheSh6bEFkckRhdGVOdW1iZXIpCmdndHNkaXNwbGF5KGRpZmYoemxBZHJEYXRlTnVtYmVyKSkKZ2d0c2Rpc3BsYXkoZGlmZih6bEFkckRhdGVOdW1iZXIsNCkpCmdndHNkaXNwbGF5KGRpZmYoZGlmZih6bEFkckRhdGVOdW1iZXIsNCksMSkpCmBgYAoKCmBgYHtyICBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojTG9nIHRyYW5zZm9ybWF0aW9uPwp6bEFycml2YWxEYXRlQ2FuY2VsZWQ9bG9nKHpBcnJpdmFsRGF0ZUNhbmNlbGVkKQpkZl9uZXdsIDwtIGRhdGEuZnJhbWUodmFsdWUgPSBhcy52ZWN0b3IoekFycml2YWxEYXRlQ2FuY2VsZWQpLAogICAgICAgICAgICAgICAgICAgICB0aW1lID0gdGltZSh6QXJyaXZhbERhdGVDYW5jZWxlZCkpCmdncGxvdChkZl9uZXdsKStnZW9tX3BvaW50KGFlcyh4PXRpbWUseT12YWx1ZSkpK2dlb21fbGluZShhZXMoeD10aW1lLHk9dmFsdWUpKSt5bGFiKCJNZWFuIEFEUiAiKStnZ3RpdGxlKCJNZWFuIEFEUiBmb3IgQXJyaXZhbCBEYXRlICBMT0cgIikreGxhYigiRGF0ZSAtIFdlZWtzIikKCiNEaWZmZXJlbmNlCmdndHNkaXNwbGF5KHpsQXJyaXZhbERhdGVDYW5jZWxlZCkKZ2d0c2Rpc3BsYXkoZGlmZih6bEFycml2YWxEYXRlQ2FuY2VsZWQpKQpnZ3RzZGlzcGxheShkaWZmKHpsQXJyaXZhbERhdGVDYW5jZWxlZCw0KSkKZ2d0c2Rpc3BsYXkoZGlmZihkaWZmKHpsQXJyaXZhbERhdGVDYW5jZWxlZCw0KSwxKSkKYGBgCgoKYGBge3IgIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiNMb2cgdHJhbnNmb3JtYXRpb24/CnpsQXJyaXZhbERhdGVDaGVja291dD1sb2coekFycml2YWxEYXRlQ2hlY2tvdXQpCmRmX25ld2wgPC0gZGF0YS5mcmFtZSh2YWx1ZSA9IGFzLnZlY3Rvcih6QXJyaXZhbERhdGVDaGVja291dCksCiAgICAgICAgICAgICAgICAgICAgIHRpbWUgPSB0aW1lKHpBcnJpdmFsRGF0ZUNoZWNrb3V0KSkKZ2dwbG90KGRmX25ld2wpK2dlb21fcG9pbnQoYWVzKHg9dGltZSx5PXZhbHVlKSkrZ2VvbV9saW5lKGFlcyh4PXRpbWUseT12YWx1ZSkpK3lsYWIoIk1lYW4gQURSICIpK2dndGl0bGUoIk1lYW4gQURSIGZvciBBcnJpdmFsIERhdGUgIExPRyAiKSt4bGFiKCJEYXRlIC0gV2Vla3MiKQoKI0RpZmZlcmVuY2UKZ2d0c2Rpc3BsYXkoemxBcnJpdmFsRGF0ZUNoZWNrb3V0KQpnZ3RzZGlzcGxheShkaWZmKHpsQXJyaXZhbERhdGVDaGVja291dCkpCmdndHNkaXNwbGF5KGRpZmYoemxBcnJpdmFsRGF0ZUNoZWNrb3V0LDQpKQpnZ3RzZGlzcGxheShkaWZmKGRpZmYoemxBcnJpdmFsRGF0ZUNoZWNrb3V0LDQpLDEpKQpgYGAKCmBgYHtyICBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojTG9nIHRyYW5zZm9ybWF0aW9uPwp6bFJlc2VydmF0aW9uRGF0ZUNhbmNlbGVkPWxvZyh6UmVzZXJ2YXRpb25EYXRlQ2FuY2VsZWQpCmRmX25ld2wgPC0gZGF0YS5mcmFtZSh2YWx1ZSA9IGFzLnZlY3Rvcih6UmVzZXJ2YXRpb25EYXRlQ2FuY2VsZWQpLAogICAgICAgICAgICAgICAgICAgICB0aW1lID0gdGltZSh6UmVzZXJ2YXRpb25EYXRlQ2FuY2VsZWQpKQpnZ3Bsb3QoZGZfbmV3bCkrZ2VvbV9wb2ludChhZXMoeD10aW1lLHk9dmFsdWUpKStnZW9tX2xpbmUoYWVzKHg9dGltZSx5PXZhbHVlKSkreWxhYigiTWVhbiBBRFIgIikrZ2d0aXRsZSgiTWVhbiBBRFIgZm9yIEFycml2YWwgRGF0ZSAgTE9HICIpK3hsYWIoIkRhdGUgLSBXZWVrcyIpCgojRGlmZmVyZW5jZQpnZ3RzZGlzcGxheSh6bFJlc2VydmF0aW9uRGF0ZUNhbmNlbGVkKQpnZ3RzZGlzcGxheShkaWZmKHpsUmVzZXJ2YXRpb25EYXRlQ2FuY2VsZWQpKQpnZ3RzZGlzcGxheShkaWZmKHpsUmVzZXJ2YXRpb25EYXRlQ2FuY2VsZWQsNCkpCmdndHNkaXNwbGF5KGRpZmYoZGlmZih6bFJlc2VydmF0aW9uRGF0ZUNhbmNlbGVkLDQpLDEpKQpgYGAKCmBgYHtyICBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojTG9nIHRyYW5zZm9ybWF0aW9uPwp6bFJlc2VydmF0aW9uRGF0ZUNoZWNrb3V0PWxvZyh6UmVzZXJ2YXRpb25EYXRlQ2hlY2tvdXQpCmRmX25ld2wgPC0gZGF0YS5mcmFtZSh2YWx1ZSA9IGFzLnZlY3Rvcih6UmVzZXJ2YXRpb25EYXRlQ2hlY2tvdXQpLAogICAgICAgICAgICAgICAgICAgICB0aW1lID0gdGltZSh6UmVzZXJ2YXRpb25EYXRlQ2hlY2tvdXQpKQpnZ3Bsb3QoZGZfbmV3bCkrZ2VvbV9wb2ludChhZXMoeD10aW1lLHk9dmFsdWUpKStnZW9tX2xpbmUoYWVzKHg9dGltZSx5PXZhbHVlKSkreWxhYigiTWVhbiBBRFIgIikrZ2d0aXRsZSgiTWVhbiBBRFIgZm9yIEFycml2YWwgRGF0ZSAgTE9HICIpK3hsYWIoIkRhdGUgLSBXZWVrcyIpCgojRGlmZmVyZW5jZQpnZ3RzZGlzcGxheSh6bFJlc2VydmF0aW9uRGF0ZUNoZWNrb3V0KQpnZ3RzZGlzcGxheShkaWZmKHpsUmVzZXJ2YXRpb25EYXRlQ2hlY2tvdXQpKQpnZ3RzZGlzcGxheShkaWZmKHpsUmVzZXJ2YXRpb25EYXRlQ2hlY2tvdXQsNCkpCmdndHNkaXNwbGF5KGRpZmYoZGlmZih6bFJlc2VydmF0aW9uRGF0ZUNoZWNrb3V0LDQpLDEpKQpgYGAKCgojIyMgTU9ERUxJTkcKCiMjIyMgRVRTICAKIyMjIyMgVFJBSU4gQU5EIFRFU1QgREFUQSBTRVQKYGBge3J9CiNTZWxlY3QgbnVtYmVyIG9mIG9ic2VydmF0aW9uIHRvIGNvbXBhcmUgZm9yZWNhc3QKY09taXQ9MjAKCiNEYXRhIFNpemUKbk9ic0Fycml2YWw9bGVuZ3RoKHpBcnJpdmFsRGF0ZU51bWJlcikKb0Fycml2YWxEYXRlTnVtYmVyIDwtIHdpbmRvdyh6QXJyaXZhbERhdGVOdW1iZXIsc3RhcnQ9aW5kZXgoekFycml2YWxEYXRlTnVtYmVyWzFdKSxlbmQ9aW5kZXgoekFycml2YWxEYXRlTnVtYmVyW25PYnNBcnJpdmFsLWNPbWl0XSkpCgojRGF0YSBTaXplCm5PYnNSZXNlcnZhdGlvbj1sZW5ndGgoelJlc2VydmF0aW9uRGF0ZU51bWJlcikKb1Jlc2VydmF0aW9uRGF0ZU51bWJlciA8LSB3aW5kb3coelJlc2VydmF0aW9uRGF0ZU51bWJlcixzdGFydD1pbmRleCh6UmVzZXJ2YXRpb25EYXRlTnVtYmVyWzFdKSxlbmQ9aW5kZXgoelJlc2VydmF0aW9uRGF0ZU51bWJlcltuT2JzUmVzZXJ2YXRpb24tY09taXRdKSkKCiNEYXRhIFNpemUKbk9ic0NhbmNlbGVkPWxlbmd0aCh6QXJyaXZhbERhdGVDYW5jZWxlZCkKb0Fycml2YWxEYXRlQ2FuY2VsZWQgPC0gd2luZG93KHpBcnJpdmFsRGF0ZUNhbmNlbGVkLHN0YXJ0PWluZGV4KHpBcnJpdmFsRGF0ZUNhbmNlbGVkWzFdKSxlbmQ9aW5kZXgoekFycml2YWxEYXRlQ2FuY2VsZWRbbk9ic0NhbmNlbGVkLWNPbWl0XSkpCgojRGF0YSBTaXplCm5PYnNDaGVja291dD1sZW5ndGgoekFycml2YWxEYXRlQ2hlY2tvdXQpCm9BcnJpdmFsRGF0ZUNoZWNrb3V0IDwtIHdpbmRvdyh6QXJyaXZhbERhdGVDaGVja291dCxzdGFydD1pbmRleCh6QXJyaXZhbERhdGVDaGVja291dFsxXSksZW5kPWluZGV4KHpBcnJpdmFsRGF0ZUNoZWNrb3V0W25PYnNDaGVja291dC1jT21pdF0pKQoKCiNEYXRhIFNpemUKbk9ic0NoZWNrb3V0PWxlbmd0aCh6UmVzZXJ2YXRpb25EYXRlQ2FuY2VsZWQpCm9SZXNlcnZhdGlvbkRhdGVDYW5jZWxlZCA8LSB3aW5kb3coelJlc2VydmF0aW9uRGF0ZUNhbmNlbGVkLHN0YXJ0PWluZGV4KHpSZXNlcnZhdGlvbkRhdGVDYW5jZWxlZFsxXSksZW5kPWluZGV4KHpSZXNlcnZhdGlvbkRhdGVDYW5jZWxlZFtuT2JzQ2hlY2tvdXQtY09taXRdKSkKCiNEYXRhIFNpemUKbk9ic0NoZWNrb3V0PWxlbmd0aCh6UmVzZXJ2YXRpb25EYXRlQ2hlY2tvdXQpCm9SZXNlcnZhdGlvbkRhdGVDaGVja291dCA8LSB3aW5kb3coelJlc2VydmF0aW9uRGF0ZUNoZWNrb3V0LHN0YXJ0PWluZGV4KHpSZXNlcnZhdGlvbkRhdGVDaGVja291dFsxXSksZW5kPWluZGV4KHpSZXNlcnZhdGlvbkRhdGVDaGVja291dFtuT2JzQ2hlY2tvdXQtY09taXRdKSkKCmBgYAoKCiMjIyMjIFRSQUlOSU5HIFRIRSBNT0RFTApgYGB7cn0KZXRzZml0QXJyaXZhbDwtZXRzKG9BcnJpdmFsRGF0ZU51bWJlciwgbGFtYmRhID0gImF1dG8iLCB1c2UuaW5pdGlhbC52YWx1ZXMgPSBUUlVFLCBubXNlID0gMzApCmV0c2ZpdFJlc2VydmF0aW9uPC1ldHMob1Jlc2VydmF0aW9uRGF0ZU51bWJlciwgbGFtYmRhID0gImF1dG8iLCB1c2UuaW5pdGlhbC52YWx1ZXMgPSBUUlVFLCBubXNlID0gMzApCgpldHNmaXRBcnJpdmFsQ2FuY2VsZWQ8LWV0cyhvQXJyaXZhbERhdGVDYW5jZWxlZCwgbGFtYmRhID0gImF1dG8iLCB1c2UuaW5pdGlhbC52YWx1ZXMgPSBUUlVFLCBubXNlID0gMzApCgpldHNmaXRBcnJpdmFsQ2hlY2tvdXQ8LWV0cyhvQXJyaXZhbERhdGVDaGVja291dCwgbGFtYmRhID0gImF1dG8iLCB1c2UuaW5pdGlhbC52YWx1ZXMgPSBUUlVFLCBubXNlID0gMzApCgpldHNmaXRSZXNlcnZhdGlvbkNhbmNlbGVkPC1ldHMob1Jlc2VydmF0aW9uRGF0ZUNhbmNlbGVkLCBsYW1iZGEgPSAiYXV0byIsIHVzZS5pbml0aWFsLnZhbHVlcyA9IFRSVUUsIG5tc2UgPSAzMCkKCmV0c2ZpdFJlc2VydmF0aW9uQ2hlY2tvdXQ8LWV0cyhvUmVzZXJ2YXRpb25EYXRlQ2hlY2tvdXQsIGxhbWJkYSA9ICJhdXRvIiwgdXNlLmluaXRpYWwudmFsdWVzID0gVFJVRSwgbm1zZSA9IDMwKQpgYGAKCiMjIyMjIEZPUkVDQVNUCmBgYHtyfQojZm9yZWNhc3QgbW9kZWwKZkFycml2YWwuZXRzPWZvcmVjYXN0KGV0c2ZpdEFycml2YWwsIGggPSAyMCkKZlJlc2VydmF0aW9uLmV0cz1mb3JlY2FzdChldHNmaXRSZXNlcnZhdGlvbiwgaCA9IDIwKQpmQ2FuY2VsZWQuZXRzPWZvcmVjYXN0KGV0c2ZpdEFycml2YWxDYW5jZWxlZCwgaCA9IDIwKQpmQ2hlY2tvdXQuZXRzPWZvcmVjYXN0KGV0c2ZpdEFycml2YWxDaGVja291dCwgaCA9IDIwKQoKZkNhbmNlbGVkUmVzZXJ2YXRpb24uZXRzPWZvcmVjYXN0KGV0c2ZpdFJlc2VydmF0aW9uQ2FuY2VsZWQsIGggPSAyMCkKZkNoZWNrb3V0UmVzZXJ2YXRpb24uZXRzPWZvcmVjYXN0KGV0c2ZpdFJlc2VydmF0aW9uQ2hlY2tvdXQsIGggPSAyMCkKYGBgCgoKCmBgYHtyfQpwbG90KGZBcnJpdmFsLmV0cykKbGluZXMod2luZG93KHpBcnJpdmFsRGF0ZU51bWJlciksdHlwZT0ibyIpCmBgYAoKCmBgYHtyfQpwbG90KGZSZXNlcnZhdGlvbi5ldHMpCmxpbmVzKHdpbmRvdyh6UmVzZXJ2YXRpb25EYXRlTnVtYmVyKSx0eXBlPSJvIikKYGBgCmBgYHtyfQpwbG90KGZDYW5jZWxlZC5ldHMpCmxpbmVzKHdpbmRvdyh6QXJyaXZhbERhdGVDYW5jZWxlZCksdHlwZT0ibyIpCmBgYApgYGB7cn0KcGxvdChmQ2hlY2tvdXQuZXRzKQpsaW5lcyh3aW5kb3coekFycml2YWxEYXRlQ2hlY2tvdXQpLHR5cGU9Im8iKQpgYGAKCmBgYHtyfQpwbG90KGZDYW5jZWxlZFJlc2VydmF0aW9uLmV0cykKbGluZXMod2luZG93KHpSZXNlcnZhdGlvbkRhdGVDYW5jZWxlZCksdHlwZT0ibyIpCmBgYAoKYGBge3J9CnBsb3QoZkNoZWNrb3V0UmVzZXJ2YXRpb24uZXRzKQpsaW5lcyh3aW5kb3coelJlc2VydmF0aW9uRGF0ZUNoZWNrb3V0KSx0eXBlPSJvIikKYGBgCiMjIyMjIEVSUk9SIE1FQVNVUkVNRU5UUwoKIyMjIyMjIEFycml2YWwgRGF0ZQpgYGB7cn0KI0FjdHVhbCBhbmQgRm9yZWNhc3QKZXJyb3JfZm9yZWNhc3RfYWN0dWFsIDwtIG1hdHJpeChjKGZBcnJpdmFsLmV0cyRtZWFuWzE6Y09taXRdLHpBcnJpdmFsRGF0ZU51bWJlclsobk9ic0Fycml2YWwtY09taXQrMSk6bk9ic0Fycml2YWxdKSxuY29sPTIpCgojTUFFCmNhdCgiTUFFOiIsbWVhbihhYnMoZXJyb3JfZm9yZWNhc3RfYWN0dWFsWywyXS1lcnJvcl9mb3JlY2FzdF9hY3R1YWxbLDFdKSkpIzEgZm9yZWNhc3QsIDIgYWN0dWFsLgojIFsxXSAzNDUuMzg2NgpjYXQoIlxuXG4iKQoKI0JJQVMKYmlhcyA9IHN1bShlcnJvcl9mb3JlY2FzdF9hY3R1YWxbLDFdLWVycm9yX2ZvcmVjYXN0X2FjdHVhbFssMl0pICogMS4wL2xlbmd0aChlcnJvcl9mb3JlY2FzdF9hY3R1YWxbLDJdKQpjYXQoJ0JpYXMgJTonLCBiaWFzKQpgYGAKCiMjIyMjIyBSZXNlcnZhdGlvbiBEYXRlCmBgYHtyfQojQWN0dWFsIGFuZCBGb3JlY2FzdAplcnJvcl9mb3JlY2FzdF9hY3R1YWwgPC0gbWF0cml4KGMoZlJlc2VydmF0aW9uLmV0cyRtZWFuWzE6Y09taXRdLHpSZXNlcnZhdGlvbkRhdGVOdW1iZXJbKG5PYnNSZXNlcnZhdGlvbi1jT21pdCsxKTpuT2JzUmVzZXJ2YXRpb25dKSxuY29sPTIpCgojTUFFCmNhdCgiTUFFOiIsbWVhbihhYnMoZXJyb3JfZm9yZWNhc3RfYWN0dWFsWywyXS1lcnJvcl9mb3JlY2FzdF9hY3R1YWxbLDFdKSkpIzEgZm9yZWNhc3QsIDIgYWN0dWFsLgojIFsxXSAzNDUuMzg2NgpjYXQoIlxuXG4iKQoKI0JJQVMKYmlhcyA9IHN1bShlcnJvcl9mb3JlY2FzdF9hY3R1YWxbLDFdLWVycm9yX2ZvcmVjYXN0X2FjdHVhbFssMl0pICogMS4wL2xlbmd0aChlcnJvcl9mb3JlY2FzdF9hY3R1YWxbLDJdKQpjYXQoJ0JpYXMgJTonLCBiaWFzKQpgYGAKCiMjIyMjIyBBcnJpdmFsIERhdGUgQ2FuY2VsZWQKYGBge3J9CiNBY3R1YWwgYW5kIEZvcmVjYXN0CmVycm9yX2ZvcmVjYXN0X2FjdHVhbCA8LSBtYXRyaXgoYyhmQ2FuY2VsZWQuZXRzJG1lYW5bMTpjT21pdF0sekFycml2YWxEYXRlQ2FuY2VsZWRbKG5PYnNDYW5jZWxlZC1jT21pdCsxKTpuT2JzQ2FuY2VsZWRdKSxuY29sPTIpCgplcnJvcl9mb3JlY2FzdF9hY3R1YWxDYW5jZWxlZCA8LSBlcnJvcl9mb3JlY2FzdF9hY3R1YWwKCiNNQUUKY2F0KCJNQUU6IixtZWFuKGFicyhlcnJvcl9mb3JlY2FzdF9hY3R1YWxbLDJdLWVycm9yX2ZvcmVjYXN0X2FjdHVhbFssMV0pKSkjMSBmb3JlY2FzdCwgMiBhY3R1YWwuCiMgWzFdIDM0NS4zODY2CmNhdCgiXG5cbiIpCgojQklBUwpiaWFzID0gc3VtKGVycm9yX2ZvcmVjYXN0X2FjdHVhbFssMV0tZXJyb3JfZm9yZWNhc3RfYWN0dWFsWywyXSkgKiAxLjAvbGVuZ3RoKGVycm9yX2ZvcmVjYXN0X2FjdHVhbFssMl0pCmNhdCgnQmlhcyAlOicsIGJpYXMpCmBgYAoKIyMjIyMjIEFycml2YWwgRGF0ZSBDaGVja291dApgYGB7cn0KI0FjdHVhbCBhbmQgRm9yZWNhc3QKZXJyb3JfZm9yZWNhc3RfYWN0dWFsIDwtIG1hdHJpeChjKGZDaGVja291dC5ldHMkbWVhblsxOmNPbWl0XSx6QXJyaXZhbERhdGVDaGVja291dFsobk9ic0NoZWNrb3V0LWNPbWl0KzEpOm5PYnNDaGVja291dF0pLG5jb2w9MikKCmVycm9yX2ZvcmVjYXN0X2FjdHVhbENoZWNrb3V0IDwtIGVycm9yX2ZvcmVjYXN0X2FjdHVhbAoKI01BRQpjYXQoIk1BRToiLG1lYW4oYWJzKGVycm9yX2ZvcmVjYXN0X2FjdHVhbFssMl0tZXJyb3JfZm9yZWNhc3RfYWN0dWFsWywxXSkpKSMxIGZvcmVjYXN0LCAyIGFjdHVhbC4KIyBbMV0gMzQ1LjM4NjYKY2F0KCJcblxuIikKCiNCSUFTCmJpYXMgPSBzdW0oZXJyb3JfZm9yZWNhc3RfYWN0dWFsWywxXS1lcnJvcl9mb3JlY2FzdF9hY3R1YWxbLDJdKSAqIDEuMC9sZW5ndGgoZXJyb3JfZm9yZWNhc3RfYWN0dWFsWywyXSkKY2F0KCdCaWFzICU6JywgYmlhcykKYGBgCgoKIyMjIyMjIENoZWNrb3V0ICsgQ2FuY2VsZWQgQXJyaXZhbCBEYXRlCmBgYHtyfQojQWN0dWFsIGFuZCBGb3JlY2FzdAplcnJvcl9mb3JlY2FzdF9hY3R1YWwgPC0gZXJyb3JfZm9yZWNhc3RfYWN0dWFsQ2FuY2VsZWQgKyBlcnJvcl9mb3JlY2FzdF9hY3R1YWxDaGVja291dAoKI01BRQpjYXQoIk1BRToiLG1lYW4oYWJzKGVycm9yX2ZvcmVjYXN0X2FjdHVhbFssMl0tZXJyb3JfZm9yZWNhc3RfYWN0dWFsWywxXSkpKSMxIGZvcmVjYXN0LCAyIGFjdHVhbC4KIyBbMV0gMzQ1LjM4NjYKY2F0KCJcblxuIikKCiNCSUFTCmJpYXMgPSBzdW0oZXJyb3JfZm9yZWNhc3RfYWN0dWFsWywxXS1lcnJvcl9mb3JlY2FzdF9hY3R1YWxbLDJdKSAqIDEuMC9sZW5ndGgoZXJyb3JfZm9yZWNhc3RfYWN0dWFsWywyXSkKY2F0KCdCaWFzICU6JywgYmlhcykKYGBgCgojIyMjIyMgUmVzZXJ2YXRpb24gRGF0ZSBDYW5jZWxlZApgYGB7cn0KI0FjdHVhbCBhbmQgRm9yZWNhc3QgCmVycm9yX2ZvcmVjYXN0X2FjdHVhbCA8LSAgbWF0cml4KGMoZlJlc2VydmF0aW9uLmV0cyRtZWFuWzE6Y09taXRdLHpSZXNlcnZhdGlvbkRhdGVDYW5jZWxlZFsobk9ic0NhbmNlbGVkLWNPbWl0KzEpOm5PYnNDYW5jZWxlZF0pLG5jb2w9MikKCmVycm9yX2ZvcmVjYXN0X2FjdHVhbENhbmNlbGVkUmVzZXJ2YXRpb24gPC0gZXJyb3JfZm9yZWNhc3RfYWN0dWFsCgojTUFFCmNhdCgiTUFFOiIsbWVhbihhYnMoZXJyb3JfZm9yZWNhc3RfYWN0dWFsWywyXS1lcnJvcl9mb3JlY2FzdF9hY3R1YWxbLDFdKSkpIzEgZm9yZWNhc3QsIDIgYWN0dWFsLgojIFsxXSAzNDUuMzg2NgpjYXQoIlxuXG4iKQoKI0JJQVMKYmlhcyA9IHN1bShlcnJvcl9mb3JlY2FzdF9hY3R1YWxbLDFdLWVycm9yX2ZvcmVjYXN0X2FjdHVhbFssMl0pICogMS4wL2xlbmd0aChlcnJvcl9mb3JlY2FzdF9hY3R1YWxbLDJdKQpjYXQoJ0JpYXMgJTonLCBiaWFzKQpgYGAKCiMjIyMjIyBSZXNlcnZhdGlvbiBEYXRlIENoZWNrb3V0CmBgYHtyfQojQWN0dWFsIGFuZCBGb3JlY2FzdAplcnJvcl9mb3JlY2FzdF9hY3R1YWwgPC0gIG1hdHJpeChjKGZDaGVja291dFJlc2VydmF0aW9uLmV0cyRtZWFuWzE6Y09taXRdLHpSZXNlcnZhdGlvbkRhdGVDaGVja291dFsobk9ic0NoZWNrb3V0LWNPbWl0KzEpOm5PYnNDaGVja291dF0pLG5jb2w9MikKCmVycm9yX2ZvcmVjYXN0X2FjdHVhbENoZWNrb3V0UmVzZXJ2YXRpb24gPC0gZXJyb3JfZm9yZWNhc3RfYWN0dWFsCgojTUFFCmNhdCgiTUFFOiIsbWVhbihhYnMoZXJyb3JfZm9yZWNhc3RfYWN0dWFsWywyXS1lcnJvcl9mb3JlY2FzdF9hY3R1YWxbLDFdKSkpIzEgZm9yZWNhc3QsIDIgYWN0dWFsLgojIFsxXSAzNDUuMzg2NgpjYXQoIlxuXG4iKQoKI0JJQVMKYmlhcyA9IHN1bShlcnJvcl9mb3JlY2FzdF9hY3R1YWxbLDFdLWVycm9yX2ZvcmVjYXN0X2FjdHVhbFssMl0pICogMS4wL2xlbmd0aChlcnJvcl9mb3JlY2FzdF9hY3R1YWxbLDJdKQpjYXQoJ0JpYXMgJTonLCBiaWFzKQpgYGAKCgojIyMjIyMgQ2hlY2tvdXQgKyBDYW5jZWxlZCBSZXNlcnZhdGlvbiBEYXRlCmBgYHtyfQojQWN0dWFsIGFuZCBGb3JlY2FzdAplcnJvcl9mb3JlY2FzdF9hY3R1YWwgPC0gZXJyb3JfZm9yZWNhc3RfYWN0dWFsQ2FuY2VsZWRSZXNlcnZhdGlvbiArIGVycm9yX2ZvcmVjYXN0X2FjdHVhbENoZWNrb3V0UmVzZXJ2YXRpb24KCiNNQUUKY2F0KCJNQUU6IixtZWFuKGFicyhlcnJvcl9mb3JlY2FzdF9hY3R1YWxbLDJdLWVycm9yX2ZvcmVjYXN0X2FjdHVhbFssMV0pKSkjMSBmb3JlY2FzdCwgMiBhY3R1YWwuCiMgWzFdIDM0NS4zODY2CmNhdCgiXG5cbiIpCgojQklBUwpiaWFzID0gc3VtKGVycm9yX2ZvcmVjYXN0X2FjdHVhbFssMV0tZXJyb3JfZm9yZWNhc3RfYWN0dWFsWywyXSkgKiAxLjAvbGVuZ3RoKGVycm9yX2ZvcmVjYXN0X2FjdHVhbFssMl0pCmNhdCgnQmlhcyAlOicsIGJpYXMpCmBgYAojIyMjIEFSSU1BCiMjIyMgVFJBSU5JTkcgVEhFIE1PREVMCmBgYHtyfQojIGZpdDFBcnJpdmFsPWF1dG8uYXJpbWEob0Fycml2YWxEYXRlTnVtYmVyLGxhbWJkYSA9IDApCmZpdDFBcnJpdmFsPWF1dG8uYXJpbWEob0Fycml2YWxEYXRlTnVtYmVyLCBsYW1iZGEgPSAwLCBzdGFydC5wID0gMCwgc3RhcnQuUSA9IDAsIG1heC5wID0gMTAsIG1heC5xID0gMTAsIHN0YXJ0LlAgPSAwLCBzdGFydC5xID0gMCxtYXguUCA9IDEwLCBtYXguUSA9IDEwLCBtYXguZCA9IDUyLCBtYXguRCA9IDUyLHNlYXNvbmFsID0gVCkKCgpzdW1tYXJ5KGZpdDFBcnJpdmFsKQojcmVzaWR1YWwgYW5hbHlzaXMKZ2d0c2Rpc3BsYXkoZml0MUFycml2YWwkcmVzaWR1YWxzKQojYm94LUxqdW5nIFRlc3QsIDMgcG9ycXXDqSBoZW1vcyBlc3RpbWFkbyAzLgpCb3gudGVzdChmaXQxQXJyaXZhbCRyZXNpZHVhbHMsbGFnPTEsIGZpdGRmPTMsIHR5cGU9IkxqIikKIyBCb3gudGVzdChmaXQxQXJyaXZhbCRyZXNpZHVhbHMsbGFnPTgsIGZpdGRmPTMsIHR5cGU9IkxqIikKIyBCb3gudGVzdChmaXQxQXJyaXZhbCRyZXNpZHVhbHMsbGFnPTEyLCBmaXRkZj0zLCB0eXBlPSJMaiIpCmBgYAoKYGBge3J9CmZpdDFSZXNlcnZhdGlvbj1hdXRvLmFyaW1hKG9SZXNlcnZhdGlvbkRhdGVOdW1iZXIsIGxhbWJkYSA9IDAsIHN0YXJ0LnAgPSAwLCBzdGFydC5RID0gMCwgbWF4LnAgPSAxMCwgbWF4LnEgPSAxMCwgc3RhcnQuUCA9IDAsIHN0YXJ0LnEgPSAwLG1heC5QID0gMTAsIG1heC5RID0gMTAsIG1heC5kID0gNTIsIG1heC5EID0gNTIsc2Vhc29uYWwgPSBUKQoKc3VtbWFyeShmaXQxUmVzZXJ2YXRpb24pCiNyZXNpZHVhbCBhbmFseXNpcwpnZ3RzZGlzcGxheShmaXQxUmVzZXJ2YXRpb24kcmVzaWR1YWxzKQojYm94LUxqdW5nIFRlc3QsIDMgcG9ycXXDqSBoZW1vcyBlc3RpbWFkbyAzLgpCb3gudGVzdChmaXQxUmVzZXJ2YXRpb24kcmVzaWR1YWxzLGxhZz00LCBmaXRkZj0zLCB0eXBlPSJMaiIpCkJveC50ZXN0KGZpdDFSZXNlcnZhdGlvbiRyZXNpZHVhbHMsbGFnPTgsIGZpdGRmPTMsIHR5cGU9IkxqIikKQm94LnRlc3QoZml0MVJlc2VydmF0aW9uJHJlc2lkdWFscyxsYWc9MTIsIGZpdGRmPTMsIHR5cGU9IkxqIikKYGBgCgpgYGB7cn0KZml0MUNhbmNlbGVkPWF1dG8uYXJpbWEob0Fycml2YWxEYXRlQ2FuY2VsZWQsIGxhbWJkYSA9IDAsIHN0YXJ0LnAgPSAwLCBzdGFydC5RID0gMCwgbWF4LnAgPSAxMCwgbWF4LnEgPSAxMCwgc3RhcnQuUCA9IDAsIHN0YXJ0LnEgPSAwLG1heC5QID0gMTAsIG1heC5RID0gMTAsIG1heC5kID0gNTIsIG1heC5EID0gNTIsc2Vhc29uYWwgPSBUKQoKc3VtbWFyeShmaXQxQ2FuY2VsZWQpCiNyZXNpZHVhbCBhbmFseXNpcwpnZ3RzZGlzcGxheShmaXQxQ2FuY2VsZWQkcmVzaWR1YWxzKQojYm94LUxqdW5nIFRlc3QsIDMgcG9ycXXDqSBoZW1vcyBlc3RpbWFkbyAzLgpCb3gudGVzdChmaXQxQ2FuY2VsZWQkcmVzaWR1YWxzLGxhZz00LCBmaXRkZj0zLCB0eXBlPSJMaiIpCkJveC50ZXN0KGZpdDFDYW5jZWxlZCRyZXNpZHVhbHMsbGFnPTgsIGZpdGRmPTMsIHR5cGU9IkxqIikKQm94LnRlc3QoZml0MUNhbmNlbGVkJHJlc2lkdWFscyxsYWc9MTIsIGZpdGRmPTMsIHR5cGU9IkxqIikKYGBgCgpgYGB7cn0KZml0MUNoZWNrb3V0PWF1dG8uYXJpbWEob0Fycml2YWxEYXRlQ2hlY2tvdXQsIGxhbWJkYSA9IDAsIHN0YXJ0LnAgPSAwLCBzdGFydC5RID0gMCwgbWF4LnAgPSAxMCwgbWF4LnEgPSAxMCwgc3RhcnQuUCA9IDAsIHN0YXJ0LnEgPSAwLG1heC5QID0gMTAsIG1heC5RID0gMTAsIG1heC5kID0gNTIsIG1heC5EID0gNTIsc2Vhc29uYWwgPSBUKQoKc3VtbWFyeShmaXQxQ2hlY2tvdXQpCiNyZXNpZHVhbCBhbmFseXNpcwpnZ3RzZGlzcGxheShmaXQxQ2hlY2tvdXQkcmVzaWR1YWxzKQojYm94LUxqdW5nIFRlc3QsIDMgcG9ycXXDqSBoZW1vcyBlc3RpbWFkbyAzLgpCb3gudGVzdChmaXQxQ2hlY2tvdXQkcmVzaWR1YWxzLGxhZz00LCBmaXRkZj0zLCB0eXBlPSJMaiIpCkJveC50ZXN0KGZpdDFDaGVja291dCRyZXNpZHVhbHMsbGFnPTgsIGZpdGRmPTMsIHR5cGU9IkxqIikKQm94LnRlc3QoZml0MUNoZWNrb3V0JHJlc2lkdWFscyxsYWc9MTIsIGZpdGRmPTMsIHR5cGU9IkxqIikKYGBgCgpgYGB7cn0KZml0MUNhbmNlbGVkUmVzZXJ2YXRpb249YXV0by5hcmltYShvUmVzZXJ2YXRpb25EYXRlQ2FuY2VsZWQsIGxhbWJkYSA9IDAsIHN0YXJ0LnAgPSAwLCBzdGFydC5RID0gMCwgbWF4LnAgPSAxMCwgbWF4LnEgPSAxMCwgc3RhcnQuUCA9IDAsIHN0YXJ0LnEgPSAwLG1heC5QID0gMTAsIG1heC5RID0gMTAsIG1heC5kID0gNTIsIG1heC5EID0gNTIsc2Vhc29uYWwgPSBUKQoKc3VtbWFyeShmaXQxQ2FuY2VsZWRSZXNlcnZhdGlvbikKI3Jlc2lkdWFsIGFuYWx5c2lzCmdndHNkaXNwbGF5KGZpdDFDYW5jZWxlZFJlc2VydmF0aW9uJHJlc2lkdWFscykKI2JveC1ManVuZyBUZXN0LCAzIHBvcnF1w6kgaGVtb3MgZXN0aW1hZG8gMy4KQm94LnRlc3QoZml0MUNhbmNlbGVkUmVzZXJ2YXRpb24kcmVzaWR1YWxzLGxhZz00LCBmaXRkZj0zLCB0eXBlPSJMaiIpCkJveC50ZXN0KGZpdDFDYW5jZWxlZFJlc2VydmF0aW9uJHJlc2lkdWFscyxsYWc9OCwgZml0ZGY9MywgdHlwZT0iTGoiKQpCb3gudGVzdChmaXQxQ2FuY2VsZWRSZXNlcnZhdGlvbiRyZXNpZHVhbHMsbGFnPTEyLCBmaXRkZj0zLCB0eXBlPSJMaiIpCmBgYApgYGB7cn0KZml0MUNoZWNrb3V0UmVzZXJ2YXRpb249YXV0by5hcmltYShvUmVzZXJ2YXRpb25EYXRlQ2hlY2tvdXQsIGxhbWJkYSA9IDAsIHN0YXJ0LnAgPSAwLCBzdGFydC5RID0gMCwgbWF4LnAgPSAxMCwgbWF4LnEgPSAxMCwgc3RhcnQuUCA9IDAsIHN0YXJ0LnEgPSAwLG1heC5QID0gMTAsIG1heC5RID0gMTAsIG1heC5kID0gNTIsIG1heC5EID0gNTIsc2Vhc29uYWwgPSBUKQoKc3VtbWFyeShmaXQxQ2hlY2tvdXRSZXNlcnZhdGlvbikKI3Jlc2lkdWFsIGFuYWx5c2lzCmdndHNkaXNwbGF5KGZpdDFDaGVja291dFJlc2VydmF0aW9uJHJlc2lkdWFscykKI2JveC1ManVuZyBUZXN0LCAzIHBvcnF1w6kgaGVtb3MgZXN0aW1hZG8gMy4KQm94LnRlc3QoZml0MUNoZWNrb3V0UmVzZXJ2YXRpb24kcmVzaWR1YWxzLGxhZz00LCBmaXRkZj0zLCB0eXBlPSJMaiIpCkJveC50ZXN0KGZpdDFDaGVja291dFJlc2VydmF0aW9uJHJlc2lkdWFscyxsYWc9OCwgZml0ZGY9MywgdHlwZT0iTGoiKQpCb3gudGVzdChmaXQxQ2hlY2tvdXRSZXNlcnZhdGlvbiRyZXNpZHVhbHMsbGFnPTEyLCBmaXRkZj0zLCB0eXBlPSJMaiIpCmBgYAojIyMjIEZPUkVDQVNUCmBgYHtyfQpmQXJyaXZhbC5hcmltYT1mb3JlY2FzdChmaXQxQXJyaXZhbCwgaCA9IDIwKQoKcGxvdChmQXJyaXZhbC5hcmltYSkKbGluZXMod2luZG93KHpBcnJpdmFsRGF0ZU51bWJlciksdHlwZT0ibyIpCgoKcmVzX0FyaW1hTWF0cml4IDwtIG1hdHJpeChjKGZBcnJpdmFsLmFyaW1hJG1lYW5bMToyMF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5kb3VibGUodGFpbCh6QXJyaXZhbERhdGVOdW1iZXIsbj0yMCkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gMikKcmVzX0FyaW1hTWF0cml4CmBgYAoKYGBge3J9CmZSZXNlcnZhdGlvbi5hcmltYT1mb3JlY2FzdChmaXQxUmVzZXJ2YXRpb24sIGggPSAyMCkKCnBsb3QoZlJlc2VydmF0aW9uLmFyaW1hKQpsaW5lcyh3aW5kb3coelJlc2VydmF0aW9uRGF0ZU51bWJlciksdHlwZT0ibyIpCgpyZXNfQXJpbWFNYXRyaXggPC0gbWF0cml4KGMoZlJlc2VydmF0aW9uLmFyaW1hJG1lYW5bMToyMF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5kb3VibGUodGFpbCh6UmVzZXJ2YXRpb25EYXRlTnVtYmVyLG49MjApKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDIpCnJlc19BcmltYU1hdHJpeApgYGAKCmBgYHtyfQpmQ2FuY2VsZWQuYXJpbWE9Zm9yZWNhc3QoZml0MUNhbmNlbGVkLCBoID0gMjApCgpwbG90KGZDYW5jZWxlZC5hcmltYSkKbGluZXMod2luZG93KHpBcnJpdmFsRGF0ZUNhbmNlbGVkKSx0eXBlPSJvIikKCnJlc19BcmltYU1hdHJpeCA8LSBtYXRyaXgoYyhmQ2FuY2VsZWQuYXJpbWEkbWVhblsxOjIwXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmRvdWJsZSh0YWlsKHpBcnJpdmFsRGF0ZUNhbmNlbGVkLG49MjApKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDIpCnJlc19BcmltYU1hdHJpeApgYGAKCmBgYHtyfQpmQ2hlY2tvdXQuYXJpbWE9Zm9yZWNhc3QoZml0MUNoZWNrb3V0LCBoID0gMjApCgpwbG90KGZDaGVja291dC5hcmltYSkKbGluZXMod2luZG93KHpBcnJpdmFsRGF0ZUNoZWNrb3V0KSx0eXBlPSJvIikKCnJlc19BcmltYU1hdHJpeCA8LSBtYXRyaXgoYyhmQ2hlY2tvdXQuYXJpbWEkbWVhblsxOjIwXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmRvdWJsZSh0YWlsKHpBcnJpdmFsRGF0ZUNoZWNrb3V0LG49MjApKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDIpCnJlc19BcmltYU1hdHJpeApgYGAKCmBgYHtyfQpmQ2FuY2VsZWQuYXJpbWFSZXNlcnZhdGlvbj1mb3JlY2FzdChmaXQxQ2FuY2VsZWRSZXNlcnZhdGlvbiwgaCA9IDIwKQoKcGxvdChmQ2FuY2VsZWQuYXJpbWFSZXNlcnZhdGlvbikKbGluZXMod2luZG93KHpSZXNlcnZhdGlvbkRhdGVDYW5jZWxlZCksdHlwZT0ibyIpCgpyZXNfQXJpbWFNYXRyaXggPC0gbWF0cml4KGMoZkNhbmNlbGVkLmFyaW1hUmVzZXJ2YXRpb24kbWVhblsxOjIwXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmRvdWJsZSh0YWlsKHpSZXNlcnZhdGlvbkRhdGVDYW5jZWxlZCxuPTIwKSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSAyKQpyZXNfQXJpbWFNYXRyaXgKYGBgCgpgYGB7cn0KZkNoZWNrb3V0LmFyaW1hUmVzZXJ2YXRpb249Zm9yZWNhc3QoZml0MUNoZWNrb3V0UmVzZXJ2YXRpb24sIGggPSAyMCkKCnBsb3QoZkNoZWNrb3V0LmFyaW1hUmVzZXJ2YXRpb24pCmxpbmVzKHdpbmRvdyh6UmVzZXJ2YXRpb25EYXRlQ2hlY2tvdXQpLHR5cGU9Im8iKQoKcmVzX0FyaW1hTWF0cml4IDwtIG1hdHJpeChjKGZDaGVja291dC5hcmltYVJlc2VydmF0aW9uJG1lYW5bMToyMF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5kb3VibGUodGFpbCh6UmVzZXJ2YXRpb25EYXRlQ2hlY2tvdXQsbj0yMCkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gMikKcmVzX0FyaW1hTWF0cml4CmBgYAoKIyMjIyBFUlJPUiBNRUFTVVJFTUVOVFMKIyMjIyMgQXJyaXZhbApgYGB7cn0KZXJyb3JfZm9yZWNhc3Q8LSBhcy5kYXRhLmZyYW1lKGZBcnJpdmFsLmFyaW1hJG1lYW5bMToyMF0pI2ZvcmVjYXN0CmVycm9yX2FjdHVhbCA8LSBhcy5kYXRhLmZyYW1lKHRhaWwoekFycml2YWxEYXRlTnVtYmVyLG49MjApKSNhY3R1YWwKZGltKGVycm9yX2FjdHVhbClbMV0KZXJyb3JfYWN0dWFsX3RvdGFsIDwtIGVycm9yX2FjdHVhbAoKI01BRQpjYXQoIk1BRToiLG1lYW4oYXMubWF0cml4KGFicyhlcnJvcl9mb3JlY2FzdCAtIGVycm9yX2FjdHVhbCkpKSkjMSBmb3JlY2FzdCwgMiBhY3R1YWwuCgpjYXQoIlxuXG4iKQojQklBUwpiaWFzID0gc3VtKGFzLm1hdHJpeChlcnJvcl9mb3JlY2FzdCAtIGVycm9yX2FjdHVhbCkpICogMS4wL2RpbShlcnJvcl9hY3R1YWwpWzFdCmNhdCgnQmlhcyAlOicsIGJpYXMpCmBgYAojIyMjIyBSZXNlcnZhdGlvbgpgYGB7cn0KZXJyb3JfZm9yZWNhc3Q8LSBhcy5kYXRhLmZyYW1lKGZSZXNlcnZhdGlvbi5hcmltYSRtZWFuWzE6MjBdKSNmb3JlY2FzdAplcnJvcl9hY3R1YWwgPC0gYXMuZGF0YS5mcmFtZSh0YWlsKHpSZXNlcnZhdGlvbkRhdGVOdW1iZXIsbj0yMCkpI2FjdHVhbApkaW0oZXJyb3JfYWN0dWFsKVsxXQplcnJvcl9hY3R1YWxfdG90YWwgPC0gZXJyb3JfYWN0dWFsCgojTUFFCmNhdCgiTUFFOiIsbWVhbihhcy5tYXRyaXgoYWJzKGVycm9yX2ZvcmVjYXN0IC0gZXJyb3JfYWN0dWFsKSkpKSMxIGZvcmVjYXN0LCAyIGFjdHVhbC4KCmNhdCgiXG5cbiIpCiNCSUFTCmJpYXMgPSBzdW0oYXMubWF0cml4KGVycm9yX2ZvcmVjYXN0IC0gZXJyb3JfYWN0dWFsKSkgKiAxLjAvZGltKGVycm9yX2FjdHVhbClbMV0KY2F0KCdCaWFzICU6JywgYmlhcykKYGBgCgojIyMjIyBDYW5jZWxlZCBBcnJpdmFsIERhdGUKYGBge3J9CmVycm9yX2ZvcmVjYXN0Q2FuY2VsZWQ8LSBhcy5kYXRhLmZyYW1lKGZDYW5jZWxlZC5hcmltYSRtZWFuWzE6MjBdKSNmb3JlY2FzdAplcnJvcl9hY3R1YWxDYW5jZWxlZCA8LSBhcy5kYXRhLmZyYW1lKHRhaWwoekFycml2YWxEYXRlQ2FuY2VsZWQsbj0yMCkpI2FjdHVhbApkaW0oZXJyb3JfYWN0dWFsQ2FuY2VsZWQpWzFdCmVycm9yX2FjdHVhbF90b3RhbCA8LSBlcnJvcl9hY3R1YWxDYW5jZWxlZAoKI01BRQpjYXQoIk1BRToiLG1lYW4oYXMubWF0cml4KGFicyhlcnJvcl9mb3JlY2FzdENhbmNlbGVkIC0gZXJyb3JfYWN0dWFsQ2FuY2VsZWQpKSkpIzEgZm9yZWNhc3QsIDIgYWN0dWFsLgoKY2F0KCJcblxuIikKI0JJQVMKYmlhcyA9IHN1bShhcy5tYXRyaXgoZXJyb3JfZm9yZWNhc3RDYW5jZWxlZCAtIGVycm9yX2FjdHVhbENhbmNlbGVkKSkgKiAxLjAvZGltKGVycm9yX2FjdHVhbENhbmNlbGVkKVsxXQpjYXQoJ0JpYXMgJTonLCBiaWFzKQpgYGAKCgojIyMjIyBDaGVja291dCBBcnJpdmFsIERhdGUKYGBge3J9CmVycm9yX2ZvcmVjYXN0Q2hlY2tvdXQ8LSBhcy5kYXRhLmZyYW1lKGZDaGVja291dC5hcmltYSRtZWFuWzE6MjBdKSNmb3JlY2FzdAplcnJvcl9hY3R1YWxDaGVja291dCA8LSBhcy5kYXRhLmZyYW1lKHRhaWwoekFycml2YWxEYXRlQ2hlY2tvdXQsbj0yMCkpI2FjdHVhbApkaW0oZXJyb3JfYWN0dWFsQ2hlY2tvdXQpWzFdCmVycm9yX2FjdHVhbF90b3RhbCA8LSBlcnJvcl9hY3R1YWxDaGVja291dAoKI01BRQpjYXQoIk1BRToiLG1lYW4oYXMubWF0cml4KGFicyhlcnJvcl9mb3JlY2FzdENoZWNrb3V0IC0gZXJyb3JfYWN0dWFsQ2hlY2tvdXQpKSkpIzEgZm9yZWNhc3QsIDIgYWN0dWFsLgoKY2F0KCJcblxuIikKI0JJQVMKYmlhcyA9IHN1bShhcy5tYXRyaXgoZXJyb3JfZm9yZWNhc3RDaGVja291dCAtIGVycm9yX2FjdHVhbENoZWNrb3V0KSkgKiAxLjAvZGltKGVycm9yX2FjdHVhbENoZWNrb3V0KVsxXQpjYXQoJ0JpYXMgJTonLCBiaWFzKQpgYGAKCiMjIyMjIENoZWNrb3V0ICsgQ2FuY2VsZWQgQXJyaXZhbCBEYXRlCmBgYHtyfQplcnJvcl9mb3JlY2FzdDwtIGVycm9yX2ZvcmVjYXN0Q2FuY2VsZWQgKyBlcnJvcl9mb3JlY2FzdENoZWNrb3V0CmVycm9yX2FjdHVhbCA8LSBlcnJvcl9hY3R1YWxDYW5jZWxlZCArIGVycm9yX2FjdHVhbENoZWNrb3V0CmRpbShlcnJvcl9hY3R1YWwpWzFdCmVycm9yX2FjdHVhbF90b3RhbCA8LSBlcnJvcl9hY3R1YWwKCiNNQUUKY2F0KCJNQUU6IixtZWFuKGFzLm1hdHJpeChhYnMoZXJyb3JfZm9yZWNhc3QgLSBlcnJvcl9hY3R1YWwpKSkpIzEgZm9yZWNhc3QsIDIgYWN0dWFsLgoKY2F0KCJcblxuIikKI0JJQVMKYmlhcyA9IHN1bShhcy5tYXRyaXgoZXJyb3JfZm9yZWNhc3QgLSBlcnJvcl9hY3R1YWwpKSAqIDEuMC9kaW0oZXJyb3JfYWN0dWFsKVsxXQpjYXQoJ0JpYXMgJTonLCBiaWFzKQpgYGAKCgojIyMjIyBDYW5jZWxlZCBSZXNlcnZhdGlvbiBEYXRlCmBgYHtyfSAKCmVycm9yX2ZvcmVjYXN0Q2FuY2VsZWQ8LSBhcy5kYXRhLmZyYW1lKGZDYW5jZWxlZC5hcmltYVJlc2VydmF0aW9uJG1lYW5bMToyMF0pI2ZvcmVjYXN0CmVycm9yX2FjdHVhbENhbmNlbGVkIDwtIGFzLmRhdGEuZnJhbWUodGFpbCh6UmVzZXJ2YXRpb25EYXRlQ2FuY2VsZWQsbj0yMCkpI2FjdHVhbApkaW0oZXJyb3JfYWN0dWFsQ2FuY2VsZWQpWzFdCmVycm9yX2FjdHVhbF90b3RhbCA8LSBlcnJvcl9hY3R1YWxDYW5jZWxlZAoKI01BRQpjYXQoIk1BRToiLG1lYW4oYXMubWF0cml4KGFicyhlcnJvcl9mb3JlY2FzdENhbmNlbGVkIC0gZXJyb3JfYWN0dWFsQ2FuY2VsZWQpKSkpIzEgZm9yZWNhc3QsIDIgYWN0dWFsLgoKY2F0KCJcblxuIikKI0JJQVMKYmlhcyA9IHN1bShhcy5tYXRyaXgoZXJyb3JfZm9yZWNhc3RDYW5jZWxlZCAtIGVycm9yX2FjdHVhbENhbmNlbGVkKSkgKiAxLjAvZGltKGVycm9yX2FjdHVhbENhbmNlbGVkKVsxXQpjYXQoJ0JpYXMgJTonLCBiaWFzKQpgYGAKCgojIyMjIyBDaGVja291dCBSZXNlcnZhdGlvbiBEYXRlCmBgYHtyfQplcnJvcl9mb3JlY2FzdENoZWNrb3V0PC0gIGFzLmRhdGEuZnJhbWUoZkNoZWNrb3V0LmFyaW1hUmVzZXJ2YXRpb24kbWVhblsxOjIwXSkjZm9yZWNhc3QKZXJyb3JfYWN0dWFsQ2hlY2tvdXQgPC0gYXMuZGF0YS5mcmFtZSh0YWlsKHpSZXNlcnZhdGlvbkRhdGVDaGVja291dCxuPTIwKSkjYWN0dWFsCmRpbShlcnJvcl9hY3R1YWxDaGVja291dClbMV0KZXJyb3JfYWN0dWFsX3RvdGFsIDwtIGVycm9yX2FjdHVhbENoZWNrb3V0CgojTUFFCmNhdCgiTUFFOiIsbWVhbihhcy5tYXRyaXgoYWJzKGVycm9yX2ZvcmVjYXN0Q2hlY2tvdXQgLSBlcnJvcl9hY3R1YWxDaGVja291dCkpKSkjMSBmb3JlY2FzdCwgMiBhY3R1YWwuCgpjYXQoIlxuXG4iKQojQklBUwpiaWFzID0gc3VtKGFzLm1hdHJpeChlcnJvcl9mb3JlY2FzdENoZWNrb3V0IC0gZXJyb3JfYWN0dWFsQ2hlY2tvdXQpKSAqIDEuMC9kaW0oZXJyb3JfYWN0dWFsQ2hlY2tvdXQpWzFdCmNhdCgnQmlhcyAlOicsIGJpYXMpCmBgYAoKIyMjIyMgQ2hlY2tvdXQgKyBDYW5jZWxlZCBSZXNlcnZhdGlvbiBEYXRlCmBgYHtyfQplcnJvcl9mb3JlY2FzdDwtIGVycm9yX2ZvcmVjYXN0Q2FuY2VsZWQgKyBlcnJvcl9mb3JlY2FzdENoZWNrb3V0CmVycm9yX2FjdHVhbCA8LSBlcnJvcl9hY3R1YWxDYW5jZWxlZCArIGVycm9yX2FjdHVhbENoZWNrb3V0CmRpbShlcnJvcl9hY3R1YWwpWzFdCmVycm9yX2FjdHVhbF90b3RhbCA8LSBlcnJvcl9hY3R1YWwKCiNNQUUKY2F0KCJNQUU6IixtZWFuKGFzLm1hdHJpeChhYnMoZXJyb3JfZm9yZWNhc3QgLSBlcnJvcl9hY3R1YWwpKSkpIzEgZm9yZWNhc3QsIDIgYWN0dWFsLgoKY2F0KCJcblxuIikKI0JJQVMKYmlhcyA9IHN1bShhcy5tYXRyaXgoZXJyb3JfZm9yZWNhc3QgLSBlcnJvcl9hY3R1YWwpKSAqIDEuMC9kaW0oZXJyb3JfYWN0dWFsKVsxXQpjYXQoJ0JpYXMgJTonLCBiaWFzKQpgYGAKCgojIyMjIEZJTkFMIFBSRURJQ1RJT04KCgojIyMjIyBUUkFJTklORyBUSEUgTU9ERUwKYGBge3J9CmV0c2ZpdEFycml2YWxDYW5jZWxlZDwtZXRzKHpBcnJpdmFsRGF0ZUNhbmNlbGVkWy1sZW5ndGgoekFycml2YWxEYXRlQ2FuY2VsZWQpXSwgbGFtYmRhID0gImF1dG8iLCB1c2UuaW5pdGlhbC52YWx1ZXMgPSBUUlVFLCBubXNlID0gMzApCmV0c2ZpdEFycml2YWxDaGVja291dDwtZXRzKHpBcnJpdmFsRGF0ZUNoZWNrb3V0Wy1sZW5ndGgoekFycml2YWxEYXRlQ2hlY2tvdXQpXSwgbGFtYmRhID0gImF1dG8iLCB1c2UuaW5pdGlhbC52YWx1ZXMgPSBUUlVFLCBubXNlID0gMzApCgoKZml0MVJlc2VydmF0aW9uPWF1dG8uYXJpbWEoelJlc2VydmF0aW9uRGF0ZU51bWJlciwgbGFtYmRhID0gMCwgc3RhcnQucCA9IDAsIHN0YXJ0LlEgPSAwLCBtYXgucCA9IDEwLCBtYXgucSA9IDEwLCBzdGFydC5QID0gMCwgc3RhcnQucSA9IDAsbWF4LlAgPSAxMCwgbWF4LlEgPSAxMCwgbWF4LmQgPSA1MiwgbWF4LkQgPSA1MixzZWFzb25hbCA9IFQpCgpgYGAKCgojIyMjIyBGT1JFQ0FTVApgYGB7cn0KI2ZvcmVjYXN0IG1vZGVsCmZDYW5jZWxlZC5ldHM9Zm9yZWNhc3QoZXRzZml0QXJyaXZhbENhbmNlbGVkLCBoID0gMjApCmZDaGVja291dC5ldHM9Zm9yZWNhc3QoZXRzZml0QXJyaXZhbENoZWNrb3V0LCBoID0gMjApCmBgYAoKYGBge3J9CnBsb3QoZkNhbmNlbGVkLmV0cyRtZWFuICsgZkNoZWNrb3V0LmV0cyRtZWFuLCB4bGltID0gYygwLDE1MCksIHlsaW0gPSBjKDEwMCwgNjAwKSwgbWFpbiA9ICdGaW5hbCBQcmVkaWN0aW9uJywgeWxhYiA9ICdOdW1iZXIgb2YgUmVzZXJ2YXRpb25zIGZvciBBcnJpdmFsIERhdGUnKQpsaW5lcyh3aW5kb3coekFycml2YWxEYXRlTnVtYmVyWy1sZW5ndGgoekFycml2YWxEYXRlTnVtYmVyKV0pLHR5cGU9Im8iKQpgYGAKCgpgYGB7cn0KZlJlc2VydmF0aW9uLmFyaW1hPWZvcmVjYXN0KGZpdDFSZXNlcnZhdGlvbiwgaCA9IDIwKQoKcGxvdChmUmVzZXJ2YXRpb24uYXJpbWEpCmxpbmVzKHdpbmRvdyh6UmVzZXJ2YXRpb25EYXRlTnVtYmVyKSx0eXBlPSJvIikKCnJlc19BcmltYU1hdHJpeCA8LSBtYXRyaXgoYyhmUmVzZXJ2YXRpb24uYXJpbWEkbWVhblsxOjIwXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmRvdWJsZSh0YWlsKHpSZXNlcnZhdGlvbkRhdGVOdW1iZXIsbj0yMCkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gMikKcmVzX0FyaW1hTWF0cml4CmBgYAo=